/// <summary>
        /// This function is used to generate the base level plate files.
        /// </summary>
        /// <param name="serializer">
        /// Image tile serializer to retrieve the tile.
        /// </param>
        private void CreateBaseLevelPlateFiles(IImageTileSerializer serializer)
        {
            int maxPlate = (int)Math.Pow(2, this.plateFileDetails.MinOverlappedLevel);

            Parallel.For(
                0,
                maxPlate,
                yPlate =>
            {
                for (int xPlate = 0; xPlate < maxPlate; xPlate++)
                {
                    // Get plate file name base don the X and Y coordinates of the plate file.
                    string plateFilePath = System.IO.Path.Combine(this.platesFolderPath, this.plateFileDetails.MinOverlappedLevel.ToString(CultureInfo.InvariantCulture), string.Format(CultureInfo.InvariantCulture, Constants.BaseLevelPlateFormat, this.plateFileDetails.MinOverlappedLevel, xPlate, yPlate));

                    // Create the plate file instance.
                    PlateFile currentPlate = new PlateFile(plateFilePath, this.plateFileDetails.LevelsPerPlate);
                    currentPlate.Create();

                    for (int level = this.plateFileDetails.LevelsPerPlate - 1; level >= 0; level--)
                    {
                        // Get the number of tiles for the given level index.
                        int numberOftiles = (int)Math.Pow(2, level);

                        // Calculate the start and end index of X axis.
                        int xStart = xPlate * numberOftiles;

                        // Calculate the start and end index of Y axis.
                        int yStart = yPlate * numberOftiles;

                        // Add each tile to the plate file.
                        for (int yIndex = 0; yIndex < numberOftiles; yIndex++)
                        {
                            for (int xIndex = 0; xIndex < numberOftiles; xIndex++)
                            {
                                TilesProcessed++;
                                if (serializer != null)
                                {
                                    Bitmap tile = serializer.Deserialize((this.maxLevels - (this.plateFileDetails.LevelsPerPlate - 1) + level), xStart + xIndex, yStart + yIndex);
                                    if (tile != null)
                                    {
                                        using (MemoryStream ms = new MemoryStream())
                                        {
                                            ms.Seek(0, SeekOrigin.Begin);
                                            tile.Save(ms, format);
                                            currentPlate.AddStream(ms, level, xIndex, yIndex);
                                        }

                                        tile.Dispose();
                                    }
                                }
                            }
                        }
                    }

                    // Update the header and close the file stream.
                    currentPlate.UpdateHeaderAndClose();
                }
            });
        }
示例#2
0
        /// <summary>
        /// This function is used to retrieve the tile from the multiple plates.
        /// </summary>
        /// <param name="level">Level of the image.</param>
        /// <param name="x">X axis image.</param>
        /// <param name="y">Y axis image.</param>
        /// <param name="platesFolder">Multiple plates folder.</param>
        /// <param name="topLevelPlateFilename">Top level plate file name.</param>
        /// <param name="plateFilenameFormat">Format for plate file name.</param>
        /// <returns>Tile image for the specified image.</returns>
        private static Stream GetTileFromMultiplePlates(int level, int x, int y, DirectoryInfo platesFolder, string topLevelPlateFilename, string plateFilenameFormat)
        {
            Stream outputStream = null;
            int    maxLevel     = GetMaxLevel(platesFolder);

            // TODO: Need to check on how to get the details on how many levels are there in a plate.
            int levelsPerPlate = 8;

            // Get the levels break up from Plates folder structure.
            List <int> plateLevels = GetPlateLevels(platesFolder);

            // Sort the plate levels. This is make sure that we can do binary search.
            plateLevels.Sort();

            // Get the index of level of the plate files (folder name) where the plate file which contains the tile for level, x and y could be found.
            // BinarySearch = The zero-based index of item in the sorted List<T>, if item is found; otherwise, a negative number that is the bitwise
            //      complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of Count.
            int plateLevelIndex = plateLevels.BinarySearch(level);

            plateLevelIndex = plateLevelIndex < 0 ? ~plateLevelIndex - 1 : plateLevelIndex;

            if (plateLevelIndex <= 0)
            {
                // This means that we need to get data from top level plate file.
                PlateFile plateFileInstance = new PlateFile(Path.Combine(platesFolder.FullName, topLevelPlateFilename), levelsPerPlate);
                outputStream = plateFileInstance.GetFileStream(level, x, y);
            }
            else if (level <= maxLevel)
            {
                // Get the minimum level number (which will be the Folder name) from the plate level index.
                int minLevel = plateLevels[plateLevelIndex];

                // Level in the local plate file.
                int plateLevel = level - minLevel;

                int    powLevDiff  = (int)Math.Pow(2, plateLevel);
                int    plateIndexX = x / powLevDiff;
                int    plateIndexY = y / powLevDiff;
                string filename    = string.Format(CultureInfo.InvariantCulture, plateFilenameFormat, minLevel, plateIndexX, plateIndexY);

                int tileIndexX = x % powLevDiff;
                int tileIndexY = y % powLevDiff;

                PlateFile plateFileInstance = new PlateFile(Path.Combine(platesFolder.FullName, minLevel.ToString(CultureInfo.InvariantCulture), filename), levelsPerPlate);
                outputStream = plateFileInstance.GetFileStream(plateLevel, tileIndexX, tileIndexY);
            }

            return(outputStream);
        }
        /// <summary>
        /// This function is used to generate the top level plate file.
        /// </summary>
        /// <param name="serializer">
        /// Image tile serializer to retrieve the tile.
        /// </param>
        private void CreateTopLevelDemPlateFile(IDemTileSerializer serializer)
        {
            string    plateFilePath = System.IO.Path.Combine(this.platesFolderPath, Constants.DEMTopLevelPlate);
            PlateFile currentPlate  = new PlateFile(plateFilePath, this.maxLevels);

            currentPlate.Create();

            for (int level = 0; level <= this.plateFileDetails.MaxOverlappedLevel; level++)
            {
                // Number of tiles in each direction at this level
                int n = (int)Math.Pow(2, level);

                // Add each tile to the plate file.
                for (int indexY = 0; indexY < n; indexY++)
                {
                    for (int indexX = 0; indexX < n; indexX++)
                    {
                        TilesProcessed++;
                        if (serializer != null)
                        {
                            short[] data = serializer.Deserialize(level, indexX, indexY);
                            if (data != null)
                            {
                                using (MemoryStream ms = new MemoryStream())
                                {
                                    ms.Seek(0, SeekOrigin.Begin);
                                    BinaryWriter bw = new BinaryWriter(ms);
                                    foreach (short value in data)
                                    {
                                        bw.Write(value);
                                    }

                                    currentPlate.AddStream(ms, level, indexX, indexY);
                                }

                                data = null;
                            }
                        }
                    }
                }
            }

            // Update the header and close the file stream.
            currentPlate.UpdateHeaderAndClose();
        }
        /// <summary>
        /// This function is used to create the plate file from already generated DEM pyramid.
        /// </summary>
        /// <param name="serializer">
        /// DEM tile serializer to retrieve the tile.
        /// </param>
        public void CreateFromDemTile(IDemTileSerializer serializer)
        {
            PlateFile currentPlate = new PlateFile(this.PlateFilePath, this.Levels);

            currentPlate.Create();

            for (int level = 0; level <= Levels; level++)
            {
                // Number of tiles in each direction at this level
                int n = (int)Math.Pow(2, level);

                // Add each tile to the plate file.
                for (int indexY = 0; indexY < n; indexY++)
                {
                    for (int indexX = 0; indexX < n; indexX++)
                    {
                        if (serializer != null)
                        {
                            short[] data = serializer.Deserialize(level, indexX, indexY);
                            if (data != null)
                            {
                                using (MemoryStream ms = new MemoryStream())
                                {
                                    ms.Seek(0, SeekOrigin.Begin);
                                    BinaryWriter bw = new BinaryWriter(ms);
                                    foreach (short value in data)
                                    {
                                        bw.Write(value);
                                    }

                                    currentPlate.AddStream(ms, level, indexX, indexY);
                                }

                                data = null;
                            }
                        }
                    }
                }
            }

            // Update the header and close the file stream.
            currentPlate.UpdateHeaderAndClose();
        }
        /// <summary>
        /// This function is used to create the plate file from already generated DEM pyramid.
        /// </summary>
        /// <param name="serializer">
        /// DEM tile serializer to retrieve the tile.
        /// </param>
        public void CreateFromDemTile(IDemTileSerializer serializer)
        {
            PlateFile currentPlate = new PlateFile(this.PlateFilePath, this.Levels);
            currentPlate.Create();

            for (int level = 0; level <= Levels; level++)
            {
                // Number of tiles in each direction at this level
                int n = (int)Math.Pow(2, level);

                // Add each tile to the plate file.
                for (int indexY = 0; indexY < n; indexY++)
                {
                    for (int indexX = 0; indexX < n; indexX++)
                    {
                        if (serializer != null)
                        {
                            short[] data = serializer.Deserialize(level, indexX, indexY);
                            if (data != null)
                            {
                                using (MemoryStream ms = new MemoryStream())
                                {
                                    ms.Seek(0, SeekOrigin.Begin);
                                    BinaryWriter bw = new BinaryWriter(ms);
                                    foreach (short value in data)
                                    {
                                        bw.Write(value);
                                    }

                                    currentPlate.AddStream(ms, level, indexX, indexY);
                                }

                                data = null;
                            }
                        }
                    }
                }
            }

            // Update the header and close the file stream.
            currentPlate.UpdateHeaderAndClose();
        }
示例#6
0
        /// <summary>
        /// This function is used to create the plate file from already generated pyramids.
        /// </summary>
        /// <param name="serializer">
        /// Image tile serializer to retrieve the tile.
        /// </param>
        public void CreateFromImageTile(IImageTileSerializer serializer)
        {
            TilesProcessed = 0;
            PlateFile currentPlate = new PlateFile(this.PlateFilePath, this.Levels);

            currentPlate.Create();

            for (int level = 0; level <= Levels; level++)
            {
                // Number of tiles in each direction at this level
                int n = (int)Math.Pow(2, level);

                // Add each tile to the plate file.
                for (int indexY = 0; indexY < n; indexY++)
                {
                    for (int indexX = 0; indexX < n; indexX++)
                    {
                        TilesProcessed++;
                        if (serializer != null)
                        {
                            Bitmap tile = serializer.Deserialize(level, indexX, indexY);
                            if (tile != null)
                            {
                                using (MemoryStream ms = new MemoryStream())
                                {
                                    ms.Seek(0, SeekOrigin.Begin);
                                    tile.Save(ms, ImageFormat.Png);
                                    currentPlate.AddStream(ms, level, indexX, indexY);
                                }

                                tile.Dispose();
                            }
                        }
                    }
                }
            }

            // Update the header and close the file stream.
            currentPlate.UpdateHeaderAndClose();
        }
        /// <summary>
        /// This function is used to generate the top level plate file.
        /// </summary>
        /// <param name="serializer">
        /// Image tile serializer to retrieve the tile.
        /// </param>
        private void CreateTopLevelPlateFile(IImageTileSerializer serializer)
        {
            string    plateFilePath = System.IO.Path.Combine(this.platesFolderPath, Constants.TopLevelPlate);
            PlateFile currentPlate  = new PlateFile(plateFilePath, this.maxLevels);

            currentPlate.Create();

            for (int level = 0; level <= this.plateFileDetails.MaxOverlappedLevel; level++)
            {
                // Number of tiles in each direction at this level
                int n = (int)Math.Pow(2, level);

                // Add each tile to the plate file.
                for (int indexY = 0; indexY < n; indexY++)
                {
                    for (int indexX = 0; indexX < n; indexX++)
                    {
                        TilesProcessed++;
                        if (serializer != null)
                        {
                            Bitmap tile = serializer.Deserialize(level, indexX, indexY);
                            if (tile != null)
                            {
                                using (MemoryStream ms = new MemoryStream())
                                {
                                    ms.Seek(0, SeekOrigin.Begin);
                                    tile.Save(ms, format);
                                    currentPlate.AddStream(ms, level, indexX, indexY);
                                }

                                tile.Dispose();
                            }
                        }
                    }
                }
            }

            // Update the header and close the file stream.
            currentPlate.UpdateHeaderAndClose();
        }
示例#8
0
        /// <summary>
        /// This function is used to retrieve the tile from the multiple plates.
        /// </summary>
        /// <param name="level">Level of the image.</param>
        /// <param name="x">X axis image.</param>
        /// <param name="y">Y axis image.</param>
        /// <param name="platesFolder">Multiple plates folder.</param>
        /// <param name="topLevelPlateFilename">Top level plate file name.</param>
        /// <param name="plateFilenameFormat">Format for plate file name.</param>
        /// <returns>Tile image for the specified image.</returns>
        private static Stream GetTileFromMultiplePlates(int level, int x, int y, DirectoryInfo platesFolder, string topLevelPlateFilename, string plateFilenameFormat)
        {
            Stream outputStream = null;
            int maxLevel = GetMaxLevel(platesFolder);

            // TODO: Need to check on how to get the details on how many levels are there in a plate.
            int levelsPerPlate = 8;

            // Get the levels break up from Plates folder structure.
            List<int> plateLevels = GetPlateLevels(platesFolder);

            // Sort the plate levels. This is make sure that we can do binary search.
            plateLevels.Sort();

            // Get the index of level of the plate files (folder name) where the plate file which contains the tile for level, x and y could be found.
            // BinarySearch = The zero-based index of item in the sorted List<T>, if item is found; otherwise, a negative number that is the bitwise
            //      complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of Count.
            int plateLevelIndex = plateLevels.BinarySearch(level);
            plateLevelIndex = plateLevelIndex < 0 ? ~plateLevelIndex - 1 : plateLevelIndex;

            if (plateLevelIndex <= 0)
            {
                // This means that we need to get data from top level plate file.
                PlateFile plateFileInstance = new PlateFile(Path.Combine(platesFolder.FullName, topLevelPlateFilename), levelsPerPlate);
                outputStream = plateFileInstance.GetFileStream(level, x, y);
            }
            else if (level <= maxLevel)
            {
                // Get the minimum level number (which will be the Folder name) from the plate level index.
                int minLevel = plateLevels[plateLevelIndex];

                // Level in the local plate file.
                int plateLevel = level - minLevel;

                int powLevDiff = (int)Math.Pow(2, plateLevel);
                int plateIndexX = x / powLevDiff;
                int plateIndexY = y / powLevDiff;
                string filename = string.Format(CultureInfo.InvariantCulture, plateFilenameFormat, minLevel, plateIndexX, plateIndexY);

                int tileIndexX = x % powLevDiff;
                int tileIndexY = y % powLevDiff;

                PlateFile plateFileInstance = new PlateFile(Path.Combine(platesFolder.FullName, minLevel.ToString(CultureInfo.InvariantCulture), filename), levelsPerPlate);
                outputStream = plateFileInstance.GetFileStream(plateLevel, tileIndexX, tileIndexY);
            }

            return outputStream;
        }
        /// <summary>
        /// Gets tile image.
        /// </summary>
        /// <param name="id">ID for specified Pyramid.</param>
        /// <param name="level">Level for the image.</param>
        /// <param name="x">X axis of the image.</param>
        /// <param name="y">Y axis of the image.</param>
        /// <returns>Stream for the tile image for the specified level, x and y axis.</returns>
        public Stream GetTile(string id, int level, int x, int y)
        {
            Stream outputStream = null;
            try
            {
                string pyramidFolderPath = Path.Combine(this.CommunityLocation, id, Constants.PyramidFolder);
                string platesFolderPath = Path.Combine(this.CommunityLocation, id, Constants.PlatesFolder);
                DirectoryInfo pyramidFolder = new DirectoryInfo(pyramidFolderPath);
                DirectoryInfo platesFolder = new DirectoryInfo(platesFolderPath);

                if (pyramidFolder.Exists)
                {
                    string pyramidPath = Path.Combine(this.CommunityLocation, id, Constants.TileImagePath);

                    string filename = string.Format(CultureInfo.InvariantCulture, pyramidPath, level, x, y, ImageFormat.Png.ToString());

                    if (File.Exists(filename))
                    {
                        using (Bitmap bmp = new Bitmap(filename, false))
                        {
                            if (bmp != null)
                            {
                                outputStream = new MemoryStream();
                                bmp.Save(outputStream, ImageFormat.Png);
                                outputStream.Seek(0, SeekOrigin.Begin);
                            }
                        }
                    }
                }
                else if (platesFolder.Exists)
                {
                    outputStream = PlateFileHelper.GetTileFromMultiplePlates(level, x, y, platesFolder.FullName);
                }
                else
                {
                    // If Pyramid folder is not available, then try to read from plate file.
                    FileInfo plateFile = pyramidFolder.Parent.GetFiles(Constants.PlateFileSearchPattern).FirstOrDefault();
                    if (plateFile != null && plateFile.Exists)
                    {
                        PlateFile plateFileInstance = new PlateFile(plateFile.FullName, level);
                        outputStream = plateFileInstance.GetFileStream(level, x, y);
                    }
                }
            }
            catch (ArgumentException ex)
            {
                ErrorHandler.LogException(ex);
                throw new FaultException(ex.Message);
            }
            catch (IOException ex)
            {
                ErrorHandler.LogException(ex);
                throw new FaultException(ex.Message);
            }
            catch (ObjectDisposedException ex)
            {
                ErrorHandler.LogException(ex);
                throw new FaultException(ex.Message);
            }

            return outputStream;
        }
        /// <summary>
        /// Gets Dem for the specified tile id.
        /// </summary>
        /// <param name="id">Tile details id.</param>
        /// <param name="level">Level of the image.</param>
        /// <param name="x">X axis image.</param>
        /// <param name="y">Y axis image.</param>
        /// <returns>Dem for the specified image.</returns>
        public Stream GetDem(string id, int level, int x, int y)
        {
            Stream outputStream = null;
            try
            {
                string pyramidFolderPath = Path.Combine(this.CommunityLocation, id, Constants.PyramidFolder);
                string demPlatesFolderPath = Path.Combine(this.CommunityLocation, id, Constants.DEMPlatesFolder);
                DirectoryInfo pyramidFolder = new DirectoryInfo(pyramidFolderPath);
                DirectoryInfo platesFolder = new DirectoryInfo(demPlatesFolderPath);

                if (pyramidFolder.Exists)
                {
                    string pyramidPath = Path.Combine(this.CommunityLocation, id, Constants.DemTilePath);

                    string filename = string.Format(CultureInfo.InvariantCulture, pyramidPath, level, x, y, Constants.DemExtension);
                    if (File.Exists(filename))
                    {
                        byte[] data = null;
                        using (Stream s = File.OpenRead(filename))
                        {
                            int length = (int)s.Length;
                            data = new byte[length];
                            s.Read(data, 0, length);
                            outputStream = new MemoryStream(data);
                        }
                    }
                }
                else if (platesFolder.Exists)
                {
                    // Get from multiple plates
                    outputStream = PlateFileHelper.GetDEMTileFromMultiplePlates(level, x, y, platesFolder.FullName);
                }
                else
                {
                    // If Pyramid folder is not available, then try to read from plate file.
                    FileInfo plateFile = pyramidFolder.Parent.GetFiles(Constants.PlateFileSearchPattern).FirstOrDefault();
                    if (plateFile != null && plateFile.Exists)
                    {
                        PlateFile plateFileInstance = new PlateFile(plateFile.FullName, level);
                        outputStream = plateFileInstance.GetFileStream(level, x, y);
                    }
                }
            }
            catch (ArgumentException ex)
            {
                ErrorHandler.LogException(ex);
                throw new FaultException(ex.Message);
            }
            catch (IOException ex)
            {
                ErrorHandler.LogException(ex);
                throw new FaultException(ex.Message);
            }
            catch (ObjectDisposedException ex)
            {
                ErrorHandler.LogException(ex);
                throw new FaultException(ex.Message);
            }

            return outputStream;
        }
示例#11
0
        /// <summary>
        /// This function is used to create the plate file from already generated pyramids.
        /// </summary>
        /// <param name="serializer">
        /// Image tile serializer to retrieve the tile.
        /// </param>
        public void CreateFromImageTile(IImageTileSerializer serializer)
        {
            TilesProcessed = 0;
            PlateFile currentPlate = new PlateFile(this.PlateFilePath, this.Levels);
            currentPlate.Create();

            for (int level = 0; level <= Levels; level++)
            {
                // Number of tiles in each direction at this level
                int n = (int)Math.Pow(2, level);

                // Add each tile to the plate file.
                for (int indexY = 0; indexY < n; indexY++)
                {
                    for (int indexX = 0; indexX < n; indexX++)
                    {
                        TilesProcessed++;
                        if (serializer != null)
                        {
                            Bitmap tile = serializer.Deserialize(level, indexX, indexY);
                            if (tile != null)
                            {
                                using (MemoryStream ms = new MemoryStream())
                                {
                                    ms.Seek(0, SeekOrigin.Begin);
                                    tile.Save(ms, ImageFormat.Png);
                                    currentPlate.AddStream(ms, level, indexX, indexY);
                                }

                                tile.Dispose();
                            }
                        }
                    }
                }
            }

            // Update the header and close the file stream.
            currentPlate.UpdateHeaderAndClose();
        }
        /// <summary>
        /// This function is used to generate the top level plate file.
        /// </summary>
        /// <param name="serializer">
        /// Image tile serializer to retrieve the tile.
        /// </param>
        private void CreateTopLevelPlateFile(IImageTileSerializer serializer)
        {
            string plateFilePath = System.IO.Path.Combine(this.platesFolderPath, Constants.TopLevelPlate);
            PlateFile currentPlate = new PlateFile(plateFilePath, this.maxLevels);
            currentPlate.Create();

            for (int level = 0; level <= this.plateFileDetails.MaxOverlappedLevel; level++)
            {
                // Number of tiles in each direction at this level
                int n = (int)Math.Pow(2, level);

                // Add each tile to the plate file.
                for (int indexY = 0; indexY < n; indexY++)
                {
                    for (int indexX = 0; indexX < n; indexX++)
                    {
                        TilesProcessed++;
                        if (serializer != null)
                        {
                            Bitmap tile = serializer.Deserialize(level, indexX, indexY);
                            if (tile != null)
                            {
                                using (MemoryStream ms = new MemoryStream())
                                {
                                    ms.Seek(0, SeekOrigin.Begin);
                                    tile.Save(ms, format);
                                    currentPlate.AddStream(ms, level, indexX, indexY);
                                }

                                tile.Dispose();
                            }
                        }
                    }
                }
            }

            // Update the header and close the file stream.
            currentPlate.UpdateHeaderAndClose();
        }
        /// <summary>
        /// This function is used to generate the base level plate files.
        /// </summary>
        /// <param name="serializer">
        /// Image tile serializer to retrieve the tile.
        /// </param>
        private void CreateBaseLevelPlateFiles(IImageTileSerializer serializer)
        {
            int maxPlate = (int)Math.Pow(2, this.plateFileDetails.MinOverlappedLevel);

            Parallel.For(
                0,
                maxPlate,
                yPlate =>
                {
                    for (int xPlate = 0; xPlate < maxPlate; xPlate++)
                    {
                        // Get plate file name base don the X and Y coordinates of the plate file.
                        string plateFilePath = System.IO.Path.Combine(this.platesFolderPath, this.plateFileDetails.MinOverlappedLevel.ToString(CultureInfo.InvariantCulture), string.Format(CultureInfo.InvariantCulture, Constants.BaseLevelPlateFormat, this.plateFileDetails.MinOverlappedLevel, xPlate, yPlate));

                        // Create the plate file instance.
                        PlateFile currentPlate = new PlateFile(plateFilePath, this.plateFileDetails.LevelsPerPlate);
                        currentPlate.Create();

                        for (int level = this.plateFileDetails.LevelsPerPlate - 1; level >= 0; level--)
                        {
                            // Get the number of tiles for the given level index.
                            int numberOftiles = (int)Math.Pow(2, level);

                            // Calculate the start and end index of X axis.
                            int xStart = xPlate * numberOftiles;

                            // Calculate the start and end index of Y axis.
                            int yStart = yPlate * numberOftiles;

                            // Add each tile to the plate file.
                            for (int yIndex = 0; yIndex < numberOftiles; yIndex++)
                            {
                                for (int xIndex = 0; xIndex < numberOftiles; xIndex++)
                                {
                                    TilesProcessed++;
                                    if (serializer != null)
                                    {
                                        Bitmap tile = serializer.Deserialize((this.maxLevels - (this.plateFileDetails.LevelsPerPlate - 1) + level), xStart + xIndex, yStart + yIndex);
                                        if (tile != null)
                                        {
                                            using (MemoryStream ms = new MemoryStream())
                                            {
                                                ms.Seek(0, SeekOrigin.Begin);
                                                tile.Save(ms, format);
                                                currentPlate.AddStream(ms, level, xIndex, yIndex);
                                            }

                                            tile.Dispose();
                                        }
                                    }
                                }
                            }
                        }

                        // Update the header and close the file stream.
                        currentPlate.UpdateHeaderAndClose();
                    }
                });
        }