Exemple #1
0
        public static MapCluster RootClusterForAnnotations(List <MapPointAnnotation> initialAnnotations, double gamma, string clusterTitle, bool showSubtitle)
        {
            // KDTree
            var boundaries = new MKMapRect(Single.PositiveInfinity, Single.PositiveInfinity, 0.0, 0.0);

            foreach (var point in initialAnnotations.Select(annotation => annotation.MapPoint))
            {
                if (point.X < boundaries.Origin.X)
                {
                    boundaries.Origin.X = point.X;
                }
                if (point.Y < boundaries.Origin.Y)
                {
                    boundaries.Origin.Y = point.Y;
                }
                if (point.X > boundaries.Origin.X + boundaries.Size.Width)
                {
                    boundaries.Size.Width = point.X - boundaries.Origin.X;
                }
                if (point.Y > boundaries.Origin.Y + boundaries.Size.Height)
                {
                    boundaries.Size.Height = point.Y - boundaries.Origin.Y;
                }
            }

            Console.WriteLine("Computing KD-tree...");
            var cluster = new MapCluster(initialAnnotations, 0, boundaries, gamma, clusterTitle, showSubtitle);

            Console.WriteLine("Computation done !");
            return(cluster);
        }
Exemple #2
0
 public ClusterAnnotation()
 {
     _cluster   = null;
     Coordinate = MapConstants.Offscreen;
     Type       = ClusterAnnotationType.Unknown;
     ShouldBeRemovedAfterAnimation = false;
 }
Exemple #3
0
 public bool IsAncestorOf(MapCluster mapCluster)
 {
     return(Depth < mapCluster.Depth && (_leftChild == mapCluster || _rightChild == mapCluster || _leftChild.IsAncestorOf(mapCluster) || _rightChild.IsAncestorOf(mapCluster)));
 }
Exemple #4
0
        public MapCluster(List <MapPointAnnotation> annotations, int depth, MKMapRect mapRect, double gamma, string clusterTitle, bool showSubtitle)
        {
            Depth         = depth;
            _mapRect      = mapRect;
            _clusterTitle = clusterTitle;
            ShowSubtitle  = showSubtitle;

            switch (annotations.Count)
            {
            case 0:
                _leftChild        = null;
                _rightChild       = null;
                Annotation        = null;
                ClusterCoordinate = new CLLocationCoordinate2D(91, 181);    // Todo: Invalid, instead of constant.
                break;

            case 1:
                _leftChild        = null;
                _rightChild       = null;
                Annotation        = annotations.Last();
                ClusterCoordinate = Annotation.Annotation.Coordinate;
                break;

            default:
            {
                Annotation = null;

                // Principal Component Analysis
                // If cov(x,y) = ∑(x-x_mean) * (y-y_mean) != 0 (covariance different from zero), we are looking for the following principal vector:
                // a (aX)
                //   (aY)
                //
                // x_ = x - x_mean ; y_ = y - y_mean
                //
                // aX = cov(x_,y_)
                //
                //
                // aY = 0.5/n * ( ∑(x_^2) + ∑(y_^2) + sqrt( (∑(x_^2) + ∑(y_^2))^2 + 4 * cov(x_,y_)^2 ) )

                // compute the means of the coordinate
                var xSum = 0.0;
                var ySum = 0.0;

                foreach (var annotation in annotations)
                {
                    xSum += annotation.MapPoint.X;
                    ySum += annotation.MapPoint.Y;
                }

                var xMean = xSum / annotations.Count;
                var yMean = ySum / annotations.Count;

                if (gamma != 1.0)
                {
                    // take gamma weight into account
                    var gammaSumX   = 0.0;
                    var gammaSumY   = 0.0;
                    var maxDistance = 0.0;
                    var meanCenter  = new MKMapPoint(xMean, yMean);

                    foreach (var annotation in annotations)
                    {
                        var distance = MKGeometry.MetersBetweenMapPoints(annotation.MapPoint, meanCenter);
                        if (distance > maxDistance)
                        {
                            maxDistance = distance;
                        }
                    }

                    var totalWeight = 0.0;
                    foreach (var annotation in annotations)
                    {
                        var point              = annotation.MapPoint;
                        var distance           = MKGeometry.MetersBetweenMapPoints(point, meanCenter);
                        var normalizedDistance = maxDistance != 0.0 ? distance / maxDistance : 1.0;
                        var weight             = Math.Pow(normalizedDistance, gamma - 1.0);
                        gammaSumX   += point.X * weight;
                        gammaSumY   += point.Y * weight;
                        totalWeight += weight;
                    }

                    xMean = gammaSumX / totalWeight;
                    yMean = gammaSumY / totalWeight;
                }
                // compute coefficients

                var sumXsquared = 0.0;
                var sumYsquared = 0.0;
                var sumXy       = 0.0;

                foreach (var annotation in annotations)
                {
                    var x = annotation.MapPoint.X - xMean;
                    var y = annotation.MapPoint.Y - yMean;
                    sumXsquared += x * x;
                    sumYsquared += y * y;
                    sumXy       += x * y;
                }

                double aX;
                double aY;

                if (Math.Abs(sumXy) / annotations.Count > MapConstants.ClusterDiscriminationPrecision)
                {
                    aX = sumXy;
                    var lambda = 0.5 * ((sumXsquared + sumYsquared) + Math.Sqrt((sumXsquared + sumYsquared) * (sumXsquared + sumYsquared) + 4 * sumXy * sumXy));
                    aY = lambda - sumXsquared;
                }
                else
                {
                    aX = sumXsquared > sumYsquared ? 1.0 : 0.0;
                    aY = sumXsquared > sumYsquared ? 0.0 : 1.0;
                }

                List <MapPointAnnotation> leftAnnotations;
                List <MapPointAnnotation> rightAnnotations;

                if (Math.Abs(sumXsquared) / annotations.Count < MapConstants.ClusterDiscriminationPrecision || Math.Abs(sumYsquared) / annotations.Count < MapConstants.ClusterDiscriminationPrecision)
                {
                    // then every x equals XMean and we have to arbitrarily choose where to put the pivotIndex
                    var pivotIndex = annotations.Count / 2;
                    leftAnnotations  = annotations.GetRange(0, pivotIndex);
                    rightAnnotations = annotations.GetRange(pivotIndex, annotations.Count - pivotIndex);
                }
                else
                {
                    // compute scalar product between the vector of this regression line and the vector
                    // (x - x(mean))
                    // (y - y(mean))
                    // the sign of this scalar product determines which cluster the point belongs to
                    leftAnnotations  = new List <MapPointAnnotation>();
                    rightAnnotations = new List <MapPointAnnotation>();

                    foreach (var annotation in annotations)
                    {
                        var point = annotation.MapPoint;
                        var positivityConditionOfScalarProduct = true;

                        // TODO: Don't know why this is here... its there in the original code. Or is it a wrong Objective-C interpretation?
                        if (true)
                        {
                            positivityConditionOfScalarProduct = (point.X - xMean) * aX + (point.Y - yMean) * aY > 0.0;
                        }
                        else
                        {
                            positivityConditionOfScalarProduct = (point.Y - yMean) > 0.0;
                        }

                        if (positivityConditionOfScalarProduct)
                        {
                            leftAnnotations.Add(annotation);
                        }
                        else
                        {
                            rightAnnotations.Add(annotation);
                        }
                    }
                }

                // compute map rects
                double xMin = float.MaxValue, xMax = 0.0f, yMin = float.MaxValue, yMax = 0.0f;

                foreach (var point in leftAnnotations.Select(annotation => annotation.MapPoint))
                {
                    if (point.X > xMax)
                    {
                        xMax = point.X;
                    }
                    if (point.Y > yMax)
                    {
                        yMax = point.Y;
                    }
                    if (point.X < xMin)
                    {
                        xMin = point.X;
                    }
                    if (point.Y < yMin)
                    {
                        yMin = point.Y;
                    }
                }

                var leftMapRect = new MKMapRect(xMin, yMin, xMax - xMin, yMax - yMin);

                xMin = float.MaxValue;
                xMax = 0.0;
                yMin = float.MaxValue;
                yMax = 0.0;

                foreach (var point in rightAnnotations.Select(annotation => annotation.MapPoint))
                {
                    if (point.X > xMax)
                    {
                        xMax = point.X;
                    }
                    if (point.Y > yMax)
                    {
                        yMax = point.Y;
                    }
                    if (point.X < xMin)
                    {
                        xMin = point.X;
                    }
                    if (point.Y < yMin)
                    {
                        yMin = point.Y;
                    }
                }

                var rightMapRect = new MKMapRect(xMin, yMin, xMax - xMin, yMax - yMin);
                ClusterCoordinate = MKMapPoint.ToCoordinate(new MKMapPoint(xMean, yMean));
                _leftChild        = new MapCluster(leftAnnotations, depth + 1, leftMapRect, gamma, clusterTitle, showSubtitle);
                _rightChild       = new MapCluster(rightAnnotations, depth + 1, rightMapRect, gamma, clusterTitle, showSubtitle);
            }

            break;
            }
        }
Exemple #5
0
 public void Reset()
 {
     Cluster    = null;
     Coordinate = MapConstants.Offscreen;
 }
Exemple #6
0
        public void SetAnnotations(List <MKAnnotation> annotations)
        {
            if (!_isSettingAnnotations)
            {
                _originalAnnotations  = annotations;
                _isSettingAnnotations = true;

                RemoveAnnotations(_clusterAnnotations.ToArray()); // TODO: Co-variant conversion

                _singleAnnotationsPool  = new List <ClusterAnnotation>();
                _clusterAnnotationsPool = new List <ClusterAnnotation>();

                int numberOfAnnotationsInPool = 2 * NumberOfClusters();

                for (int i = 0; i < numberOfAnnotationsInPool; i++)
                {
                    var annotation = new ClusterAnnotation {
                        Type = ClusterAnnotationType.Leaf
                    };
                    _singleAnnotationsPool.Add(annotation);

                    annotation = new ClusterAnnotation {
                        Type = ClusterAnnotationType.Cluster
                    };
                    _clusterAnnotationsPool.Add(annotation);
                }

                AddAnnotations(_singleAnnotationsPool.ToArray());  // TODO: Co-variant conversion
                AddAnnotations(_clusterAnnotationsPool.ToArray()); // TODO: Co-variant conversion

                var temp = _singleAnnotationsPool;
                temp.AddRange(_clusterAnnotationsPool);
                _clusterAnnotations = temp;

                const double gamma        = 1.0;
                const string clusterTitle = "%d elements";

                // TODO: Run async, like the original, performance optimizing
                //await Task.Run(async () =>
                //{
                var mapPointAnnotations = new List <MapPointAnnotation>();

                foreach (var annotation in annotations)
                {
                    var mapPointAnnotation = new MapPointAnnotation(annotation);
                    mapPointAnnotations.Add(mapPointAnnotation);
                }

                const bool shouldShowSubtitle = true;
                _rootMapCluster = MapCluster.RootClusterForAnnotations(mapPointAnnotations, gamma, clusterTitle, shouldShowSubtitle);

                // TODO: When implementing async; run on main thread from here, uses interface objects.
                ClusterInMapRect(VisibleMapRect);

                _isSettingAnnotations = false;

                if (_annotationsToBeSet != null)
                {
                    var newAnnotations = _annotationsToBeSet;
                    _annotationsToBeSet = null;
                    SetAnnotations(newAnnotations);
                }

                //});
            }
            else
            {
                _annotationsToBeSet = annotations;
            }
        }