public WtmlCollection(string name, string thumbnailUrl, string textureTilePath, int numberOfLevels, ProjectionTypes type, Boundary inputBoundary) { this.Name = name; this.PlaceName = name; this.FolderName = name; this.TextureTilePath = textureTilePath; this.NumberOfLevels = numberOfLevels; this.ProjectionType = type; this.ThumbnailUrl = thumbnailUrl; // Assign default values. this.BaseDegreesPerTile = type == ProjectionTypes.Toast ? 90 : 360; this.BandPass = BandPasses.Visible; this.DataSetType = DataSetTypes.Earth; this.DemTilePath = string.Empty; this.FileType = ".png"; this.QuadTreeMap = string.Empty; if (inputBoundary != null) { // Calculate average latitude and longitude. this.Latitude = (inputBoundary.Bottom + inputBoundary.Top) / 2; this.Longitude = (inputBoundary.Right + inputBoundary.Left) / 2; } }
/// <summary> /// Compute tile coordinates for a specified level. Computation is done by using tiles from the previous level. /// </summary> /// <param name="tiles">List of tiles already computed for previous levels.</param> /// <param name="preferredRegion">Region of interest.</param> /// <param name="level">Current level for which the tile coorinates has to be computed.</param> private static Dictionary<int, List<Tile>> ComputeTiles(Dictionary<int, List<Tile>> tiles, Boundary preferredRegion, int level) { if (level == 0) { return tiles; } List<Tile> points = tiles[level - 1]; tiles.Add(level, new List<Tile>()); int xmin, xmax, ymin, ymax; foreach (Tile point in points) { xmin = (int)point.X * 2; xmax = xmin + 1; ymin = (int)point.Y * 2; ymax = ymin + 1; for (int y = ymin; y <= ymax; y++) { for (int x = xmin; x <= xmax; x++) { if (ToastHelper.IsDesiredTile(preferredRegion, level, x, y)) { tiles[level].Add(new Tile(x, y)); } } } } return tiles; }
public static Dictionary<int, List<Tile>> ComputeTileCoordinates(Boundary preferredRegion, int maxLevel) { var tiles = new Dictionary<int, List<Tile>>(); tiles.Add(0, new List<Tile> { new Tile(0, 0) }); for (int level = 1; level <= maxLevel; level++) { tiles = ComputeTiles(tiles, preferredRegion, level); } return tiles; }
/// <summary> /// Compute the maximum level of detail that this image supports. /// </summary> /// <param name="imageHeight">Image height (in Pixels).</param> /// <param name="imageWidth">Image width (in Pixels).</param> /// <param name="inputBoundary">Input boundary.</param> /// <returns>Max level for the input image</returns> public static int CalculateMaximumLevel(long imageHeight, long imageWidth, Boundary inputBoundary) { if (inputBoundary == null) { throw new ArgumentNullException("inputBoundary"); } // Approach: // 1. Based on the input coordinates, calculate how much pixel represent each latitude of the given input image. // 2. Then image height for the whole world is (Pixels Representing an latitude) * 180. // 3. Once we have the total image height, then we can calculate the Number of levels as //// “MaxLevel = Math.Ceiling(Math.Log((image height for the whole world) / 256 ) / Math.Log(2.0))” double actualImageHeight = 180 * (imageHeight / (inputBoundary.Bottom - inputBoundary.Top)); double actualImageWidth = 360 * (imageWidth / (inputBoundary.Right - inputBoundary.Left)); // If the image height is less than 256 , max level is 0. int maxLevelHeight = (actualImageHeight >= 256) ? (int)Math.Ceiling(Math.Log(actualImageHeight / Constants.TileSize) / Math.Log(2.0)) : 0; int maxLevelWidth = (actualImageWidth >= 256) ? (int)Math.Ceiling(Math.Log(actualImageWidth / Constants.TileSize) / Math.Log(2.0)) : 0; return (maxLevelHeight > maxLevelWidth) ? maxLevelHeight : maxLevelWidth; }
/// <summary> /// Initializes a new instance of the EquirectangularGridMap class. /// </summary> /// <param name="inputGrid"> /// Input grid. /// </param> /// <param name="boundary"> /// Input boundary co-ordinates /// </param> public EquirectangularGridMap(IGrid inputGrid, Boundary boundary) { if (inputGrid == null) { throw new ArgumentNullException("inputGrid"); } if (boundary == null) { throw new ArgumentNullException("boundary"); } longitudeDelta = boundary.Right - boundary.Left; if (longitudeDelta > 360.0) { throw new ArgumentException("Longitude range must be less than 360 degrees.", "boundary"); } if (longitudeDelta <= 0.0) { throw new ArgumentException("Longitudes must be increasing from left to right.", "boundary"); } latitudeDelta = boundary.Top - boundary.Bottom; if (latitudeDelta >= 0.0) { throw new ArgumentException("Latitudes must be increasing from top to bottom.", "boundary"); } if (latitudeDelta < -180.0) { throw new ArgumentException("Latitude range must be less than 180 degrees.", "boundary"); } this.minimumLongitude = boundary.Left; this.maximumLatitude = boundary.Bottom; this.InputGrid = inputGrid; this.InputBoundary = boundary; }
/// <summary> /// Processes the input equirectangular dataset. /// </summary> /// <param name="inputGrid"> /// Input image path. /// </param> /// <param name="outputDir"> /// Output directory where pyramid is generated. /// </param> /// <param name="projection"> /// Projection type. /// </param> private static void ProcessEquirectangularGrid(string inputGrid, string outputDir, ProjectionTypes projection) { Trace.TraceInformation("{0}: Reading dataset..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); ImageFormat imageFormat = ImageFormat.Png; // Read and parse glacier bay dataset. // Define a color map with relief shading implemented. var datagridDetails = DataGridHelper.LoadFromFile(inputGrid); // Build a data grid using the input data set var dataGrid = new DataGrid(datagridDetails.Data, false); // Build the grid map for equirectangular projection using the data grid and boundary co-ordinates var equirectangularGridMap = new EquirectangularGridMap(dataGrid, datagridDetails.Boundary); // Build the color map using equirectangular projection grid map var dataColorMap = new ShadedReliefColorMap(equirectangularGridMap); var maximumLevelsOfDetail = 15; // Define an instance of ITileCreator to create image tiles. ITileCreator imageTileCreator = TileCreatorFactory.CreateImageTileCreator(dataColorMap, projection, outputDir); // Define an instance of ITileCreator to create DEM tiles. // Define serialization mechanism for storing and retrieving DEM tiles. ITileCreator demTileCreator = TileCreatorFactory.CreateDemTileCreator(dataColorMap, projection, outputDir); // MultiTile creator encapsulates image and DEM tile creators. var multiTileCreator = new MultiTileCreator(new Collection<ITileCreator>() { imageTileCreator, demTileCreator }, projection); // Define boundary for the region. var boundary = new Boundary(datagridDetails.Boundary.Left, datagridDetails.Boundary.Top, datagridDetails.Boundary.Right, datagridDetails.Boundary.Bottom); if (projection == ProjectionTypes.Toast) { boundary.Left += 180.0; boundary.Right += 180.0; } // Generate base tiles and fill up the pyramid. var tileGenerator = new TileGenerator(multiTileCreator); tileGenerator.Generate(maximumLevelsOfDetail, boundary); // Path of Mercator and Toast DEM tile server. const string MercatorDemTilePath = @"http://(web server address)?Q={0},{1},{2},Mercator,dem2178"; const string ToastDemTilePath = @"http://(web server address)?Q={0},{1},{2},Toast,dem1033"; string fileName = Path.GetFileNameWithoutExtension(inputGrid); // Generate Thumbnail Images. Trace.TraceInformation("{0}: Building Thumbnail image..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); ImageTileSerializer tileSerializer = new ImageTileSerializer(TileHelper.GetDefaultImageTilePathTemplate(outputDir), ImageFormat.Png); string thumbnailFile = Path.Combine(outputDir, fileName + ".jpeg"); TileHelper.GenerateThumbnail(tileSerializer.GetFileName(0, 0, 0), 96, 45, thumbnailFile, ImageFormat.Jpeg); // Create and save WTML file. string textureTilePath = WtmlCollection.GetWtmlTextureTilePath(TileHelper.GetDefaultImageTilePathTemplate(outputDir), imageFormat.ToString()); var inputBoundary = new Boundary(datagridDetails.Boundary.Left, datagridDetails.Boundary.Top, datagridDetails.Boundary.Right, datagridDetails.Boundary.Bottom); WtmlCollection wtmlCollection = new WtmlCollection(fileName, thumbnailFile, textureTilePath, maximumLevelsOfDetail, projection, inputBoundary); wtmlCollection.ZoomLevel = 0.2; wtmlCollection.IsElevationModel = true; wtmlCollection.DemTilePath = projection == ProjectionTypes.Mercator ? MercatorDemTilePath : ToastDemTilePath; string path = Path.Combine(outputDir, fileName + ".wtml"); wtmlCollection.Save(path); Trace.TraceInformation("{0}: Collection successfully generated.", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); }
/// <summary> /// Generates all the tiles at the level specified and fills up the pyramid above for a given region of the world. /// </summary> /// <param name="level"> /// Base level of zoom. /// </param> /// <param name="boundary"> /// Bounding coordinates of the region. /// </param> /// <remarks> /// This API should be used only for a sparse region of the world. /// The overhead involved in computing the relevant tiles is negligible for a sparse region. /// </remarks> public void Generate(int level, Boundary boundary) { this.tilesProcessed = 0; this.BuildLevel(level, boundary); this.BuildParentLevels(level, boundary); }
/// <summary> /// Fills up the pyramid of tiles above the specified level. /// </summary> /// <param name="level"> /// Base level of zoom. /// </param> /// <param name="boundary"> /// Bounding coordinates of the region. /// </param> public void BuildParentLevels(int level, Boundary boundary) { if (boundary == null) { throw new ArgumentNullException("boundary"); } if (this.tileCreator.ProjectionType == ProjectionTypes.Toast) { this.toastProjectionCoordinates = ToastHelper.ComputeTileCoordinates(boundary, level); foreach (int k in Enumerable.Range(1, level).Select(i => level - i)) { Trace.TraceInformation("{0}: Filling up pyramid at level {1}..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture), k); CurrentLevel = k; Parallel.ForEach( this.toastProjectionCoordinates[k], this.ParallelOptions, tile => { this.tileCreator.CreateParent(k, tile.X, tile.Y); Interlocked.Increment(ref this.tilesProcessed); this.ParallelOptions.CancellationToken.ThrowIfCancellationRequested(); }); } } else { int tileXMin, tileXMax, tileYMin, tileYMax; foreach (int k in Enumerable.Range(1, level).Select(i => level - i)) { CurrentLevel = k; tileXMin = Helper.GetMercatorXTileFromLongitude(boundary.Left, k); tileXMax = Helper.GetMercatorXTileFromLongitude(boundary.Right, k); tileYMin = Helper.GetMercatorYTileFromLatitude(boundary.Bottom, k); tileYMax = Helper.GetMercatorYTileFromLatitude(boundary.Top, k); BuildParentLevels(k, tileXMin, tileXMax, tileYMin, tileYMax); } } }
/// <summary> /// Generates all the tiles at the level specified for a given region of the world. /// </summary> /// <param name="level"> /// Base level of zoom. /// </param> /// <param name="boundary"> /// Bounding coordinates of the region. /// </param> /// <remarks> /// This API should be used only for a sparse region of the world. /// The overhead involved in computing the relevant tiles is negligible for a sparse region. /// </remarks> public void BuildLevel(int level, Boundary boundary) { if (boundary == null) { throw new ArgumentNullException("boundary"); } Trace.TraceInformation("{0}: Building image at level {1}..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture), level); CurrentLevel = level; if (this.tileCreator.ProjectionType == ProjectionTypes.Toast) { this.toastProjectionCoordinates = ToastHelper.ComputeTileCoordinates(boundary, level); Parallel.ForEach( this.toastProjectionCoordinates[level], this.ParallelOptions, tile => { this.tileCreator.Create(level, tile.X, tile.Y); Interlocked.Increment(ref this.tilesProcessed); this.ParallelOptions.CancellationToken.ThrowIfCancellationRequested(); }); } else { int tileXMin = Helper.GetMercatorXTileFromLongitude(boundary.Left, level); int tileXMax = Helper.GetMercatorXTileFromLongitude(boundary.Right, level); int tileYMin = Helper.GetMercatorYTileFromLatitude(boundary.Bottom, level); int tileYMax = Helper.GetMercatorYTileFromLatitude(boundary.Top, level); // If both xmin(ymin) and xmax(ymax) are equal, // the loop should run atleast for ONE time, Hence incrementing the Xmax(Ymax) by 1 tileXMax = (tileXMin == tileXMax) ? tileXMax + 1 : tileXMax; tileYMax = (tileYMin == tileYMax) ? tileYMax + 1 : tileYMax; tileXMax = (int)Math.Min(tileXMax + 1, Math.Pow(2, level)); tileYMax = (int)Math.Min(tileYMax + 1, Math.Pow(2, level)); Parallel.For( tileYMin, tileYMax, this.ParallelOptions, y => { for (int x = tileXMin; x < tileXMax; x++) { this.tileCreator.Create(level, x, y); Interlocked.Increment(ref this.tilesProcessed); this.ParallelOptions.CancellationToken.ThrowIfCancellationRequested(); } }); } }
/// <summary> /// This event is raised when the create pyramid function is called. /// </summary> /// <param name="sender"> /// create ImageWorker. /// </param> /// <param name="e"> /// DoWork EventArgs. /// </param> private void OnCreateImageWorkerDoWork(object sender, DoWorkEventArgs e) { ImagePyramidDetails inputDetails = e.Argument as ImagePyramidDetails; if (inputDetails != null) { this.CheckCancel(e); NotifyMessage(PyramidGenerationSteps.LoadingImage, PyramidGenerationStatus.Started); this.CheckCancel(e); NotifyMessage(PyramidGenerationSteps.PyramidGeneration, PyramidGenerationStatus.Started); // Define ITileCreator instance for creating image tiles. // If the input projection type and output projection type is same then we need to tile the image. Core.ITileCreator tileCreator = null; // Calculates the estimated thumbnail generation time Stopwatch thumbnailGenerationStopwatch = new Stopwatch(); thumbnailGenerationStopwatch.Start(); if (inputDetails.InputProjection == InputProjections.EquiRectangular) { // Read and initialize the image as an IGrid. var imageGrid = new Core.ImageGrid(inputDetails.InputImagePath, true); var equirectangularGridMap = new Core.EquirectangularGridMap(imageGrid, inputDetails.InputBoundary); var imageColorMap = new Core.ImageColorMap(equirectangularGridMap); tileCreator = Core.TileCreatorFactory.CreateImageTileCreator( imageColorMap, inputDetails.OutputProjection, inputDetails.OutputDirectory); } else { Core.IImageTileSerializer serializer = new Core.ImageTileSerializer( Core.TileHelper.GetDefaultImageTilePathTemplate(inputDetails.OutputDirectory), ImageFormat.Png); tileCreator = new Core.TileChopper(inputDetails.InputImagePath, serializer, inputDetails.OutputProjection, inputDetails.InputBoundary); } thumbnailGenerationStopwatch.Stop(); // Thumbnail estimated time is time required to load the image and then create the thumbnail with graphics this.ThumbnailEstimatedTime = thumbnailGenerationStopwatch.ElapsedMilliseconds * Constants.ThumbnailMultiplier; // Define plumbing for looping through all the tiles to be created for base image and pyramid. tileGenerator = new Core.TileGenerator(tileCreator); this.cancellationToken = new CancellationTokenSource(); tileGenerator.ParallelOptions.CancellationToken = this.cancellationToken.Token; this.CheckCancel(e); // Define bounds of the image. Image is assumed to cover the entire world. // If not, change the coordinates accordingly. // For Mercator projection, longitude spans from -180 to +180 and latitude from 90 to -90. Core.Boundary gridBoundary = new Core.Boundary( inputDetails.InputBoundary.Left, inputDetails.InputBoundary.Top, inputDetails.InputBoundary.Right, inputDetails.InputBoundary.Bottom); if (inputDetails.OutputProjection == ProjectionTypes.Toast) { // For Toast projection, longitude spans from 0 to +360 and latitude from 90 to -90. gridBoundary.Left += 180; gridBoundary.Right += 180; } // Start building base image and the pyramid. tileGenerator.Generate(inputDetails.Level, gridBoundary); if (inputDetails.IsGeneratePlate) { NotifyMessage(PyramidGenerationSteps.PlateFileGeneration, PyramidGenerationStatus.Started); this.CheckCancel(e); // Generate Plate file. Core.ImageTileSerializer pyramid = new Core.ImageTileSerializer( Core.TileHelper.GetDefaultImageTilePathTemplate(inputDetails.OutputDirectory), ImageFormat.Png); plateGenerator = new Core.PlateFileGenerator( System.IO.Path.Combine(inputDetails.OutputDirectory, inputDetails.OutputFilename + ".plate"), inputDetails.Level, ImageFormat.Png); plateGenerator.CreateFromImageTile(pyramid); } this.CheckCancel(e); NotifyMessage(PyramidGenerationSteps.ThumbnailGeneration, PyramidGenerationStatus.Started); // Generate Thumbnail Images. string thumbnailFile = System.IO.Path.Combine(inputDetails.OutputDirectory, inputDetails.OutputFilename + ".jpeg"); Core.TileHelper.GenerateThumbnail(inputDetails.InputImagePath, 96, 45, thumbnailFile, ImageFormat.Jpeg); this.CheckCancel(e); NotifyMessage(PyramidGenerationSteps.WTMLGeneration, PyramidGenerationStatus.Started); // Get the path of image tiles created and save it in WTML file. string pyramidPath = Core.WtmlCollection.GetWtmlTextureTilePath(Core.TileHelper.GetDefaultImageTilePathTemplate(inputDetails.OutputDirectory), ImageFormat.Png.ToString()); Core.Boundary inputBoundary = new Core.Boundary( inputDetails.InputBoundary.Left, inputDetails.InputBoundary.Top, inputDetails.InputBoundary.Right, inputDetails.InputBoundary.Bottom); // Create and save WTML collection file. Core.WtmlCollection wtmlCollection = new Core.WtmlCollection(inputDetails.OutputFilename, thumbnailFile, pyramidPath, inputDetails.Level, inputDetails.OutputProjection, inputBoundary); wtmlCollection.Credit = inputDetails.Credits; wtmlCollection.CreditUrl = inputDetails.CreditsURL; string path = System.IO.Path.Combine(inputDetails.OutputDirectory, inputDetails.OutputFilename + ".wtml"); wtmlCollection.Save(path); } }
/// <summary> /// Check whether a given x,y tile coodinate belongs/overlap with the prefered regions' coordinates. /// </summary> /// <param name="preferredRegion">Region of preference.</param> /// <param name="level">Level at which the tile coordinate has to be examined.</param> /// <param name="tileX">Tile X coordinate.</param> /// <param name="tileY">Tile Y coordinate.</param> /// <returns>Boolean indicating the tile coordinates belongs/overlaps with the region of interest.</returns> private static bool IsDesiredTile(Boundary preferredRegion, int level, int tileX, int tileY) { OctTileMap octMap = new OctTileMap(level, tileX, tileY); Boundary tileBound = new Boundary(octMap.RaMin, octMap.DecMin, octMap.RaMax, octMap.DecMax); return IsBoundOrOverlap(tileBound, preferredRegion); }
public void ValidateToastHelper() { int maxLevel = 15; var boundary = new Boundary(-136.40173 + 180, 58.35286, -135.91382 + 180, 58.75443); var tiles = ToastHelper.ComputeTileCoordinates(boundary, maxLevel); Assert.AreEqual(tiles.Count, maxLevel + 1); }
/// <summary> /// Initializes a new instance of the TileChopper class /// </summary> /// <param name="fileName"> /// Full path of the image. /// </param> /// <param name="serializer"> /// Tile serializer. /// </param> /// <param name="projectionType"> /// Projection type. /// </param> /// <param name="inputBoundary"> /// Input boundary. /// </param> public TileChopper(string fileName, IImageTileSerializer serializer, ProjectionTypes projectionType, Boundary inputBoundary) { if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException("fileName"); } try { this.TileSerializer = serializer; this.projectionType = projectionType; this.inputBoundary = inputBoundary; // Create a bitmap object and read the color pixels into the grid. using (Bitmap inputImage = new Bitmap(fileName)) { this.Initialize(inputImage); } } catch (OutOfMemoryException) { throw; } catch { string message = "An error occurred while reading input image file. Check the path and try again."; throw new InvalidOperationException(message); } }
/// <summary> /// Processes a list of input image tiles and generates pyramid for level N. /// </summary> /// <param name="inputFilePath">Xml file with list of image tile information.</param> /// <param name="outputDir">Output directory where the image tiles of pyramid has to be stored.</param> /// <param name="projection">Projection to be used.</param> private static void ProcessMultipartEquirectangularImage(string inputFilePath, string outputDir, ProjectionTypes projection) { ImageFormat imageFormat = ImageFormat.Png; Trace.TraceInformation("{0}: Reading image..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); // Get the list of equirectangular images input. string[,] imageTiles = Program.GetInputImageList(inputFilePath); // Set the grid boundaries var imageBoundary = new Boundary(-180, -90, 180, 90); // Build an image grid using the input images var imageGrid = new ImageGrid(imageTiles, true); // Build the grid map for equirectangular projection using the image grid and boundary co-ordinates var equirectangularGridMap = new EquirectangularGridMap(imageGrid, imageBoundary); // Build the color map using equirectangular projection grid map var imageColorMap = new ImageColorMap(equirectangularGridMap); var maximumLevelsOfDetail = TileHelper.CalculateMaximumLevel(imageGrid.Height, imageGrid.Width, imageBoundary); // Define ITileCreator instance for creating image tiles. ITileCreator tileCreator = TileCreatorFactory.CreateImageTileCreator(imageColorMap, projection, outputDir); // Define plumbing for looping through all the tiles to be created for base image and pyramid. var tileGenerator = new TileGenerator(tileCreator); // Start building base image and the pyramid. Trace.TraceInformation("{0}: Building base and parent levels...", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); tileGenerator.Generate(maximumLevelsOfDetail); string fileName = Path.GetFileNameWithoutExtension(inputFilePath); // Generate Plate file. Trace.TraceInformation("{0}: Building Plate file...", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); ImageTileSerializer pyramid = new ImageTileSerializer(TileHelper.GetDefaultImageTilePathTemplate(outputDir), ImageFormat.Png); PlateFileGenerator plateGenerator = new PlateFileGenerator( Path.Combine(outputDir, fileName + ".plate"), maximumLevelsOfDetail, ImageFormat.Png); plateGenerator.CreateFromImageTile(pyramid); // Generate Thumbnail Images. Trace.TraceInformation("{0}: Building Thumbnail image..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); string thumbnailFile = Path.Combine(outputDir, fileName + ".jpeg"); TileHelper.GenerateThumbnail(pyramid.GetFileName(0, 0, 0), 96, 45, thumbnailFile, ImageFormat.Jpeg); // Get the path of image tiles created and save it in WTML file. Trace.TraceInformation("{0}: Building WTML file..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); string pyramidPath = WtmlCollection.GetWtmlTextureTilePath(TileHelper.GetDefaultImageTilePathTemplate(outputDir), imageFormat.ToString()); // Create and save WTML collection file. WtmlCollection wtmlCollection = new WtmlCollection(fileName, thumbnailFile, pyramidPath, maximumLevelsOfDetail, projection); string path = Path.Combine(outputDir, fileName + ".wtml"); wtmlCollection.Save(path); Trace.TraceInformation("{0}: Collection successfully generated.", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); }
/// <summary> /// Processes a list of input image tiles and generates pyramid for level N. /// </summary> /// <param name="inputFilePath">Xml file with list of image tile information.</param> /// <param name="outputDir">Output directory where the image tiles of pyramid has to be stored.</param> /// <param name="projection">Projection to be used.</param> /// <param name="inputBoundary">Input image boundary.</param> private static void ProcessMultipartEquirectangularImage(string inputFilePath, string outputDir, ProjectionTypes projection, Boundary inputBoundary) { ImageFormat imageFormat = ImageFormat.Png; Trace.TraceInformation("{0}: Reading image..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); // Get the list of equirectangular images input. string[,] imageTiles = Program.GetInputImageList(inputFilePath); // Check if the image is circular. double longitudeDelta = inputBoundary.Right - inputBoundary.Left; bool circular = (360.0 - longitudeDelta) < 0.000001; // Build an image grid using the input images var imageGrid = new ImageGrid(imageTiles, circular); // Build the grid map for equirectangular projection using the image grid and boundary co-ordinates var equirectangularGridMap = new EquirectangularGridMap(imageGrid, inputBoundary); // Build the color map using equirectangular projection grid map var imageColorMap = new ImageColorMap(equirectangularGridMap); var maximumLevelsOfDetail = TileHelper.CalculateMaximumLevel(imageGrid.Height, imageGrid.Width, inputBoundary); // Define ITileCreator instance for creating image tiles. ITileCreator tileCreator = TileCreatorFactory.CreateImageTileCreator(imageColorMap, projection, outputDir); // Define bounds of the image. Image is assumed to cover the entire world. // If not, change the coordinates accordingly. // For Mercator projection, longitude spans from -180 to +180 and latitude from 90 to -90. Boundary gridBoundary = new Boundary(inputBoundary.Left, inputBoundary.Top, inputBoundary.Right, inputBoundary.Bottom); if (projection == ProjectionTypes.Toast) { // For Toast projection, longitude spans from 0 to +360 and latitude from 90 to -90. gridBoundary.Left += 180; gridBoundary.Right += 180; } // Define plumbing for looping through all the tiles to be created for base image and pyramid. var tileGenerator = new TileGenerator(tileCreator); // Start building base image and the pyramid. Trace.TraceInformation("{0}: Building base and parent levels...", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); tileGenerator.Generate(maximumLevelsOfDetail, gridBoundary); string fileName = Path.GetFileNameWithoutExtension(inputFilePath); // Generate Plate file. Trace.TraceInformation("{0}: Building Plate file...", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); ImageTileSerializer pyramid = new ImageTileSerializer(TileHelper.GetDefaultImageTilePathTemplate(outputDir), ImageFormat.Png); PlateFileGenerator plateGenerator = new PlateFileGenerator( Path.Combine(outputDir, fileName + ".plate"), maximumLevelsOfDetail, ImageFormat.Png); plateGenerator.CreateFromImageTile(pyramid); // Generate Thumbnail Images. Trace.TraceInformation("{0}: Building Thumbnail image..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); string thumbnailFile = Path.Combine(outputDir, fileName + ".jpeg"); TileHelper.GenerateThumbnail(pyramid.GetFileName(0, 0, 0), 96, 45, thumbnailFile, ImageFormat.Jpeg); // Get the path of image tiles created and save it in WTML file. Trace.TraceInformation("{0}: Building WTML file..", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); string pyramidPath = WtmlCollection.GetWtmlTextureTilePath(TileHelper.GetDefaultImageTilePathTemplate(outputDir), imageFormat.ToString()); // Create and save WTML collection file. WtmlCollection wtmlCollection = new WtmlCollection(fileName, thumbnailFile, pyramidPath, maximumLevelsOfDetail, projection, inputBoundary); string path = Path.Combine(outputDir, fileName + ".wtml"); wtmlCollection.Save(path); Trace.TraceInformation("{0}: Collection successfully generated.", DateTime.Now.TimeOfDay.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture)); }
/// <summary> /// Checks if two bounding coordinates overlaps each other. /// </summary> /// <param name="region1">First region to be compared.</param> /// <param name="region2">Second region to be compared.</param> /// <returns>True/false indicating the overlap.</returns> private static bool IsBoundOrOverlap(Boundary region1, Boundary region2) { // Complete containment check, either region1 might engulf region2 or vice versa. // Check if region1 bounds point region2 OR region2 bounds region1 if (region1.Left <= region2.Left && region1.Right >= region2.Right && region1.Top <= region2.Top && region1.Bottom >= region2.Bottom) { // Region1 bounds/surrounds region2 return true; } else if (region2.Left <= region1.Left && region2.Right >= region1.Right && region2.Top <= region1.Top && region2.Bottom >= region1.Bottom) { // Region2 bounds/surrounds region1 return true; } // Overlap check. if (((region2.Left >= region1.Left && region2.Left <= region1.Right) || (region2.Right >= region1.Left && region2.Right <= region1.Right)) && ((region2.Top >= region1.Top && region2.Top <= region1.Bottom) || (region2.Bottom >= region1.Top && region2.Bottom <= region1.Bottom))) { // Now region2 coordinates overlaps(some coordinates of region2 lies inside of region1) with region1 return true; } if (((region1.Left >= region2.Left && region1.Left <= region2.Right) || (region1.Right >= region2.Left && region1.Right <= region2.Right)) && ((region1.Top >= region2.Top && region1.Top <= region2.Bottom) || (region1.Bottom >= region2.Top && region1.Bottom <= region2.Bottom))) { // Now region1 coordinates overlaps(some region1 coordinates lies inside of region2) with region2 return true; } return false; }