/// <summary>
        /// Generates the indices of the segment.
        /// </summary>
        /// <param name="parentMortonCode">The Morton code of the parent segment.</param>
        /// <param name="startRowIndex">Start row index.</param>
        /// <param name="endRowIndex">End row index.</param>
        /// <param name="startColumnIndex">Start column index.</param>
        /// <param name="endColumnIndex">End column index.</param>
        /// <param name="step">The step number.</param>
        private void GenerateIndices(Double parentMortonCode, Int32 startRowIndex, Int32 endRowIndex, Int32 startColumnIndex, Int32 endColumnIndex, Int32 step)
        {
            Double[]     spectralValues;
            List <Int32> indices = new List <Int32>();
            Segment      segment = new Segment(Raster.NumberOfBands, Statistics);

            for (Int32 rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++)
            {
                for (Int32 columnIndex = startColumnIndex; columnIndex <= endColumnIndex; columnIndex++)
                {
                    indices.Add(rowIndex * Raster.NumberOfColumns + columnIndex);
                    spectralValues = new Double[Raster.NumberOfBands];
                    spectralValues = Raster.GetFloatValues(rowIndex, columnIndex);
                    segment.AddFloatValues(spectralValues);
                }
            }

            if (indices.Count != 0)
            {
                segment.MortonCode = parentMortonCode * 10 + step;
                _segmentToIndexDictionary.Add(segment, indices);
                foreach (Int32 index in indices)
                {
                    _indexToSegmentDictionary[index] = segment;
                }
            }
            Count = _segmentToIndexDictionary.Count;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="SegmentCollection" /> class.
        /// </summary>
        /// <param name="other">The other segment collection.</param>
        /// <param name="statistics">The spectral statistics.</param>
        /// <exception cref="System.ArgumentNullException">The other segment collection is null.</exception>
        public SegmentCollection(SegmentCollection other, SpectralStatistics statistics)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other", "The other segment collection is null.");
            }

            Raster     = other.Raster;
            Count      = other.Count;
            Statistics = other.Statistics | statistics;
            _indexToSegmentDictionary = new Dictionary <Int32, Segment>();
            _segmentToIndexDictionary = new Dictionary <Segment, List <Int32> >();

            foreach (Segment otherSegment in other._segmentToIndexDictionary.Keys)
            {
                List <Int32> otherIndices = other._segmentToIndexDictionary[otherSegment];

                Segment segment;
                if (Statistics == other.Statistics)
                {
                    segment = new Segment(otherSegment);
                    otherIndices.ForEach(x => _indexToSegmentDictionary.Add(x, segment));
                }
                else
                {
                    Int32 rowIndex    = otherIndices[0] / Raster.NumberOfColumns;
                    Int32 columnIndex = otherIndices[0] % Raster.NumberOfColumns;

                    if (Raster.Format == RasterFormat.Integer)
                    {
                        segment = new Segment(Raster.GetValues(rowIndex, columnIndex), Statistics);

                        for (Int32 i = 0; i < other._segmentToIndexDictionary[otherSegment].Count; i++)
                        {
                            _indexToSegmentDictionary.Add(otherIndices[i], segment);
                            rowIndex    = otherIndices[i] / Raster.NumberOfColumns;
                            columnIndex = otherIndices[i] % Raster.NumberOfColumns;

                            segment.AddValues(Raster.GetValues(rowIndex, columnIndex));
                        }
                    }
                    else
                    {
                        segment = new Segment(Raster.GetFloatValues(rowIndex, columnIndex), Statistics);

                        for (Int32 i = 0; i < other._segmentToIndexDictionary[otherSegment].Count; i++)
                        {
                            _indexToSegmentDictionary.Add(otherIndices[i], segment);
                            rowIndex    = otherIndices[i] / Raster.NumberOfColumns;
                            columnIndex = otherIndices[i] % Raster.NumberOfColumns;

                            segment.AddFloatValues(Raster.GetFloatValues(rowIndex, columnIndex));
                        }
                    }
                }

                _segmentToIndexDictionary.Add(segment, new List <Int32>(otherIndices));
            }
        }
        /// <summary>
        /// Merges the specified segments.
        /// </summary>
        /// <param name="segment">The segment.</param>
        /// <param name="rowIndex">The row index.</param>
        /// <param name="columnIndex">The column index.</param>
        private void ApplyMergeSegments(Segment segment, Int32 index)
        {
            segment.AddFloatValues(Raster.GetFloatValues(index / Raster.NumberOfColumns, index % Raster.NumberOfColumns));
            _indexToSegmentDictionary[index] = segment;

            _segmentToIndexDictionary[segment].Add(index);

            Count--;
        }
        /// <summary>
        /// Merges the segments at the specified indices.
        /// </summary>
        /// <param name="firstRowIndex">The first row index.</param>
        /// <param name="firstColumnIndex">The first column index.</param>
        /// <param name="secondRowIndex">The second row index.</param>
        /// <param name="secondColumnIndex">The second column index.</param>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// The first row index is less than 0.
        /// or
        /// The first row index is equal to or greater than the number of rows.
        /// or
        /// The first column index is less than 0.
        /// or
        /// The first column index is equal to or greater than the number of columns.
        /// or
        /// The second row index is less than 0.
        /// or
        /// The second row index is equal to or greater than the number of rows.
        /// or
        /// The second column index is less than 0.
        /// or
        /// The second column index is equal to or greater than the number of columns.
        /// </exception>
        public virtual void MergeSegments(Int32 firstRowIndex, Int32 firstColumnIndex, Int32 secondRowIndex, Int32 secondColumnIndex)
        {
            if (firstRowIndex < 0)
            {
                throw new ArgumentOutOfRangeException("firstRowIndex", "The first row index is less than 0.");
            }
            if (firstRowIndex >= Raster.NumberOfRows)
            {
                throw new ArgumentOutOfRangeException("firstRowIndex", "The first row index is equal to or greater than the number of rows.");
            }
            if (firstColumnIndex < 0)
            {
                throw new ArgumentOutOfRangeException("firstColumnIndex", "The first column index is less than 0.");
            }
            if (firstColumnIndex >= Raster.NumberOfColumns)
            {
                throw new ArgumentOutOfRangeException("firstColumnIndex", "The first column index is equal to or greater than the number of columns.");
            }
            if (secondRowIndex < 0)
            {
                throw new ArgumentOutOfRangeException("secondRowIndex", "The second row index is less than 0.");
            }
            if (secondRowIndex >= Raster.NumberOfRows)
            {
                throw new ArgumentOutOfRangeException("secondRowIndex", "The second row index is equal to or greater than the number of rows.");
            }
            if (secondColumnIndex < 0)
            {
                throw new ArgumentOutOfRangeException("secondColumnIndex", "The second column index is less than 0.");
            }
            if (secondColumnIndex >= Raster.NumberOfColumns)
            {
                throw new ArgumentOutOfRangeException("secondColumnIndex", "The second column index is equal to or greater than the number of columns.");
            }

            // query the sets
            Int32 firstIndex  = firstRowIndex * Raster.NumberOfColumns + firstColumnIndex;
            Int32 secondIndex = secondRowIndex * Raster.NumberOfColumns + secondColumnIndex;

            if (firstIndex == secondIndex)
            {
                return;
            }

            // apply merge on the available set
            if (!_indexToSegmentDictionary.ContainsKey(firstIndex))
            {
                if (!_indexToSegmentDictionary.ContainsKey(secondIndex))
                {
                    Segment segment = new Segment(Raster.GetFloatValues(firstRowIndex, firstColumnIndex), Statistics);
                    segment.AddFloatValues(Raster.GetFloatValues(secondRowIndex, secondColumnIndex));

                    _indexToSegmentDictionary.Add(firstIndex, segment);
                    _indexToSegmentDictionary.Add(secondIndex, segment);
                    _segmentToIndexDictionary.Add(segment, new List <Int32> {
                        firstIndex, secondIndex
                    });

                    Count--;
                }
                else
                {
                    ApplyMergeSegments(_indexToSegmentDictionary[secondIndex], firstIndex);
                }
            }
            else if (!_indexToSegmentDictionary.ContainsKey(secondIndex))
            {
                ApplyMergeSegments(_indexToSegmentDictionary[firstIndex], secondIndex);
            }
            else // or merge the sets
            {
                Segment firstSegment  = _indexToSegmentDictionary[firstIndex];
                Segment secondSegment = _indexToSegmentDictionary[secondIndex];
                if (firstSegment == secondSegment)
                {
                    return;
                }

                if (firstSegment.Count > secondSegment.Count)
                {
                    ApplyMergeSegments(firstSegment, secondSegment);
                }
                else
                {
                    ApplyMergeSegments(secondSegment, firstSegment);
                }
            }
        }