/// <summary> Documentation in progress... </summary>
        /// <param name="cluster"> Documentation in progress... </param>
        public void AddCluster(LineCluster <T> cluster)
        {
            SumX1 += cluster.SumX1;
            SumY1 += cluster.SumY1;
            SumX2 += cluster.SumX2;
            SumY2 += cluster.SumY2;

            NumLines      += cluster.NumLines;
            Aggregate1    += cluster.Aggregate1;
            Aggregate2    += cluster.Aggregate2;
            AggregateLine += cluster.AggregateLine;

            Tags.AddRange(cluster.Tags);
        }
        /// <summary> Documentation in progress... </summary>
        public void Cluster()
        {
            var baseClusters = new Dictionary <LineTile, LineCluster <T> >();

            // calculate cluster size for base level
            double clusterSize = referenceSize / (1 << maxLevel);

            // add all points to base cluster
            foreach (LineInfo <T> pointInfo in points)
            {
                // calculate tile key for a point
                int tileX1  = (int)(pointInfo.X1 / clusterSize);
                int tileY1  = (int)(pointInfo.Y1 / clusterSize);
                int tileX2  = (int)(pointInfo.X2 / clusterSize);
                int tileY2  = (int)(pointInfo.Y2 / clusterSize);
                var tmpTile = new LineTile {
                    X1 = tileX1, Y1 = tileY1, X2 = tileX2, Y2 = tileY2
                };

                // try to get the cluster from the dictionary.
                // If not, create a new one
                if (!baseClusters.TryGetValue(tmpTile, out var cluster))
                {
                    cluster = new LineCluster <T>(tmpTile.X1, tmpTile.Y1, tmpTile.X2, tmpTile.Y2);
                    baseClusters[tmpTile] = cluster;
                }

                // add point to cluster
                cluster.AddPoint(pointInfo);
            }

            // add base cluster at max level
            clusterLevels[maxLevel] = baseClusters;

            // now build the higher clusters recursively
            for (int i = maxLevel - 1; i >= minLevel; i--)
            {
                var childClusters = new Dictionary <LineTile, LineCluster <T> >();

                foreach (LineCluster <T> baseCluster in baseClusters.Values)
                {
                    // calculate tile key of the child cluster
                    int tileX1  = baseCluster.TileX1 / 2;
                    int tileY1  = baseCluster.TileY1 / 2;
                    int tileX2  = baseCluster.TileX2 / 2;
                    int tileY2  = baseCluster.TileY2 / 2;
                    var tmpTile = new LineTile {
                        X1 = tileX1, Y1 = tileY1, X2 = tileX2, Y2 = tileY2
                    };

                    // try to get the cluster from the dictionary.
                    // If not, create a new one
                    if (!childClusters.TryGetValue(tmpTile, out var childCluster))
                    {
                        childCluster           = new LineCluster <T>(tmpTile.X1, tmpTile.Y1, tmpTile.X2, tmpTile.Y2);
                        childClusters[tmpTile] = childCluster;
                    }

                    // add base cluster info to child cluster
                    childCluster.AddCluster(baseCluster);
                }

                // add at tile level
                clusterLevels[i] = childClusters;

                // next loop with child cluster as base cluster
                baseClusters = childClusters;
            }

            foreach (KeyValuePair <int, Dictionary <LineTile, LineCluster <T> > > clusterLevel in clusterLevels)
            {
                var clusterLevel1 = new Dictionary <Tile, List <LineCluster <T> > >();
                var clusterLevel2 = new Dictionary <Tile, List <LineCluster <T> > >();
                clusterLevels1.Add(clusterLevel.Key, clusterLevel1);
                clusterLevels2.Add(clusterLevel.Key, clusterLevel2);

                foreach (KeyValuePair <LineTile, LineCluster <T> > lineTile in clusterLevel.Value)
                {
                    var tile1 = new Tile {
                        X = lineTile.Key.X1, Y = lineTile.Key.Y1
                    };
                    if (!clusterLevel1.ContainsKey(tile1))
                    {
                        clusterLevel1.Add(tile1, new List <LineCluster <T> >());
                    }
                    clusterLevel1[tile1].Add(lineTile.Value);

                    var tile2 = new Tile {
                        X = lineTile.Key.X2, Y = lineTile.Key.Y2
                    };
                    if (!clusterLevel2.ContainsKey(tile2))
                    {
                        clusterLevel2.Add(tile2, new List <LineCluster <T> >());
                    }
                    clusterLevel2[tile2].Add(lineTile.Value);
                }
            }
        }