public static Image <Rgb24> DrawOneScaledIndexSpectrogramTile(
            LdSpectrogramConfig config,
            IndexGenerationData indexGenerationData,
            Dictionary <string, IndexProperties> indexProperties,
            TimeSpan startTime,
            TimeSpan dataScale,
            TimeSpan imageScale,
            int superTileImageWidth,
            Dictionary <string, double[, ]> spectra,
            string basename,
            ImageChrome chromeOption)
        {
            Contract.Requires(!spectra.IsNullOrEmpty());

            // calculate data duration from column count of abitrary matrix
            var      matrix       = spectra.First().Value;
            int      columnCount  = matrix.GetLength(1);
            TimeSpan dataDuration = TimeSpan.FromSeconds(columnCount * dataScale.TotalSeconds);

            var analysisStartTime = indexGenerationData.RecordingStartDate.Value.TimeOfDay.Add(indexGenerationData.AnalysisStartOffset);

            TimeSpan offsetTime        = TimeSpan.Zero;
            TimeSpan imageDuration     = TimeSpan.FromTicks(superTileImageWidth * imageScale.Ticks);
            TimeSpan halfImageDuration = TimeSpan.FromTicks(superTileImageWidth * imageScale.Ticks / 2);

            if (startTime < TimeSpan.Zero)
            {
                offsetTime = TimeSpan.Zero - startTime;
                startTime  = TimeSpan.Zero;
            }

            TimeSpan endTime = startTime + imageDuration;

            if (endTime > dataDuration)
            {
                endTime = dataDuration;
            }

            // get the plain unchromed spectrogram
            var ldSpectrogram = ZoomCommon.DrawIndexSpectrogramCommon(
                config,
                indexGenerationData,
                indexProperties,
                startTime,
                endTime,
                dataScale,
                imageScale,
                superTileImageWidth,
                spectra,
                basename);

            if (chromeOption == ImageChrome.Without)
            {
                return(ldSpectrogram);
            }

            int nyquist = 22050 / 2;

            if (indexGenerationData.SampleRateResampled > 0)
            {
                nyquist = indexGenerationData.SampleRateResampled / 2;
            }

            int hertzInterval = 1000;

            if (config != null)
            {
                hertzInterval = config.YAxisTicInterval;
            }

            string title = $"ZOOM SCALE={imageScale.TotalSeconds}s/pixel";

            var titleBar = ZoomFocusedSpectrograms.DrawTitleBarOfZoomSpectrogram(title, ldSpectrogram.Width);

            startTime    += analysisStartTime;
            ldSpectrogram = ZoomFocusedSpectrograms.FrameZoomSpectrogram(
                ldSpectrogram,
                titleBar,
                startTime,
                imageScale,
                config.XAxisTicInterval,
                nyquist,
                hertzInterval);

            // create the base image
            var image = Drawing.NewImage(ldSpectrogram.Width, ldSpectrogram.Height, Color.DarkGray);

            var xOffset = (int)(offsetTime.Ticks / imageScale.Ticks);

            image.Mutate(g1 =>
            {
                g1.DrawImage(ldSpectrogram, new Point(xOffset, 0), 1);
            });

            return(image);
        }
        /// <summary>
        /// THIS IS ENTRY METHOD FOR TILING SPECTROGRAMS.
        /// </summary>
        public static void DrawTiles(
            AnalysisIoInputDirectory io,
            ZoomParameters common,
            string analysisTag)
        {
            Log.Info("Begin Draw Super Tiles");
            Contract.Requires(common != null, "common can not be null");
            Contract.Requires(common.SpectrogramZoomingConfig != null, "SpectrogramZoomingConfig can not be null");

            var zoomConfig = common.SpectrogramZoomingConfig;
            LdSpectrogramConfig ldsConfig = common.SpectrogramZoomingConfig.LdSpectrogramConfig;
            var distributions             = common.IndexDistributions;
            var indexGenerationData       = common.IndexGenerationData;
            var indexProperties           = zoomConfig.IndexProperties;

            string fileStem = common.OriginalBasename;

            // scales for false color index spectrograms images in seconds per pixel.
            double[] indexScales = zoomConfig.SpectralIndexScale;

            // default scales for standard (FFT) spectrograms in seconds per pixel.
            double[] standardScales = zoomConfig.SpectralFrameScale;

            ValidateScales(indexScales, indexGenerationData.IndexCalculationDuration.TotalSeconds);

            var shouldRenderStandardScale = !standardScales.IsNullOrEmpty();

            if (!shouldRenderStandardScale)
            {
                Log.Warn("Standard spectrograms will not be rendered");
            }

            var allImageScales = indexScales.Concat(standardScales).ToArray();

            Log.Info("Tiling at scales: " + allImageScales.ToCommaSeparatedList());

            // determine what naming format to use for tiles
            var(namingPattern, alignmentPadding) = GetTilingProfile(
                common,
                zoomConfig,
                indexGenerationData,
                indexScales.Max());

            // pad out image so it produces a whole number of tiles
            // this solves the asymmetric right padding of short audio files
            // var paddedWidth = (int)(Math.Ceiling(zoomConfig.TileWidth / xNominalUnitScale) * xNominalUnitScale);

            // create a new tiler
            // pass it scales for x and y-axis
            // also pass it unit scale relations (between unit scale and unit height/width) to use as a reference point
            var tiler = new Tiler(
                new DirectoryInfo(io.OutputBase.FullName),
                namingPattern,
                xScales: new SortedSet <double>(allImageScales),
                xUnitScale: XNominalUnitScale,
                unitWidth: 1440,
                yScales: new SortedSet <double>(allImageScales.Select(x => 1.0)),
                yUnitScale: 1.0,
                unitHeight: namingPattern.TileHeight);

            var(spectra, filteredIndexProperties) = ZoomCommon.LoadSpectra(io, analysisTag, fileStem, zoomConfig.LdSpectrogramConfig, indexProperties);

            // false color index tiles
            GenerateIndexSpectrogramTiles(
                indexScales,
                ldsConfig,
                filteredIndexProperties,
                zoomConfig,
                spectra,
                indexGenerationData,
                fileStem,
                namingPattern,
                tiler,
                alignmentPadding);

            // standard fft frame spectrograms
            if (shouldRenderStandardScale)
            {
                GenerateStandardSpectrogramTiles(
                    spectra,
                    indexGenerationData,
                    ldsConfig,
                    filteredIndexProperties,
                    zoomConfig,
                    standardScales,
                    fileStem,
                    namingPattern,
                    tiler,
                    alignmentPadding);
            }

            Log.Success("Tiling complete");
        }