private void SaveStitchedImageAeroFile(XmlSerializer xmlSerializer, StitchedImage stitchedImage, string path) { using (TextWriter tw = new StreamWriter(path + stitchedImage.FileName + ".aero")) { xmlSerializer.Serialize(tw, stitchedImage); } }
public async Task GenerateAFSFilesAsync(AFS2GridSquare afs2GridSquare, string stitchedTilesDirectory, string afsGridSquareDirectory, IProgress <AFSFileGeneratorProgress> progress) { await Task.Run(() => { var afsFileGeneratorProgress = new AFSFileGeneratorProgress(); StitchedImage firstStitchedImageAeroFile = null; // The number of stiched tiles should always be pretty manageable so we can get a list of filenames if (Directory.Exists(stitchedTilesDirectory)) { string[] stitchedImagesAeroFiles = Directory.GetFiles(stitchedTilesDirectory, "*.aero"); int i = 0; foreach (string aeroFilename in stitchedImagesAeroFiles) { try { StitchedImage stitchedImageAeroFile; using (StreamReader reader = new StreamReader(aeroFilename)) { stitchedImageAeroFile = (StitchedImage)xmlSerializer.Deserialize(reader); reader.Close(); } if (i == 0) { firstStitchedImageAeroFile = stitchedImageAeroFile; } double stepsPerPixelX = Math.Abs((stitchedImageAeroFile.WestLongitude - stitchedImageAeroFile.EastLongitude) / stitchedImageAeroFile.Width); double stepsPerPixelY = -Math.Abs((stitchedImageAeroFile.NorthLatitude - stitchedImageAeroFile.SouthLatitude) / stitchedImageAeroFile.Height); var aidFile = new AIDFile(); aidFile.ImageFile = stitchedImageAeroFile.FileName + "." + stitchedImageAeroFile.ImageExtension; aidFile.FlipVertical = false; aidFile.StepsPerPixelX = stepsPerPixelX; aidFile.StepsPerPixelY = stepsPerPixelY; aidFile.X = stitchedImageAeroFile.WestLongitude; aidFile.Y = stitchedImageAeroFile.NorthLatitude; var aidFileStr = aidFile.ToString(); string path = stitchedTilesDirectory + stitchedImageAeroFile.FileName + ".aid"; log.InfoFormat("Writing AID file {0}", path); File.WriteAllText(path, aidFileStr); } catch (Exception ex) { } i++; } if (firstStitchedImageAeroFile != null) { this.GenerateTMCFile(afs2GridSquare, stitchedTilesDirectory, afsGridSquareDirectory, firstStitchedImageAeroFile); } else { var messageBox = new CustomMessageBox("No stiched images found for this grid square and this image detail (zoom) level.\nRun the 'Download Image Tiles' and 'Stitch Image Tiles' actions first.", "AeroScenery", MessageBoxIcon.Error); messageBox.ShowDialog(); } } }); }
private void GenerateTMCFile(AFS2GridSquare afs2GridSquare, string stitchedTilesDirectory, string afsGridSquareDirectory, StitchedImage firstStitchedImageAeroFile) { // Create directories for Geoconvert output if they do not exist. // Better to do this here in case anyone wants to run Geoconvert manually var geoConvertRawDirectory = String.Format("{0}-geoconvert-raw\\", firstStitchedImageAeroFile.ZoomLevel); var geoConvertTTCDirectory = String.Format("{0}-geoconvert-ttc\\", firstStitchedImageAeroFile.ZoomLevel); var geoConvertRawPath = afsGridSquareDirectory + geoConvertRawDirectory; var geoConvertTTCPath = afsGridSquareDirectory + geoConvertTTCDirectory; if (!Directory.Exists(geoConvertRawPath)) { Directory.CreateDirectory(geoConvertRawPath); } if (!Directory.Exists(geoConvertTTCPath)) { Directory.CreateDirectory(geoConvertTTCPath); } var tmcFile = new TMCFile(); tmcFile.AlwaysOverwrite = true; tmcFile.DoHeightmaps = false; tmcFile.FolderDestinationRaw = geoConvertRawPath; tmcFile.FolderDestinationTTC = geoConvertTTCPath; tmcFile.FolderSourceFiles = stitchedTilesDirectory; tmcFile.WriteImagesWithMask = AeroSceneryManager.Instance.Settings.GeoConvertWriteImagesWithMask.Value; tmcFile.WriteRawFiles = AeroSceneryManager.Instance.Settings.GeoConvertWriteRawFiles.Value; tmcFile.WriteTTCFiles = true; // All TMC regions will have the same lat / lon max and min // Create a template Region here to base other regions off TMCRegion tmcRegionTemplate = new TMCRegion(); // Really NW Corner tmcRegionTemplate.LatMin = afs2GridSquare.NorthLatitude; tmcRegionTemplate.LonMin = afs2GridSquare.WestLongitude; // Realy SE Corner tmcRegionTemplate.LatMax = afs2GridSquare.SouthLatitude; tmcRegionTemplate.LonMax = afs2GridSquare.EastLongitude; tmcFile.Regions = this.GenerateTMCFileRegions(tmcRegionTemplate); var tmcFileStr = tmcFile.ToString(); var filenameParts = firstStitchedImageAeroFile.FileName.Split('_'); var tmcFilename = String.Format("{0}_{1}_{2}", filenameParts[0], filenameParts[1], filenameParts[2]); string path = String.Format("{0}{1}.tmc", stitchedTilesDirectory, tmcFilename); File.WriteAllText(path, tmcFileStr); }
public async Task StitchImageTilesAsync(string tileDownloadDirectory, string stitchedTilesDirectory, bool deleteOriginals, IProgress <TileStitcherProgress> progress) { await Task.Run(() => { var tileStitcherProgress = new TileStitcherProgress(); this.xmlSerializer = new XmlSerializer(typeof(ImageTile)); this.stitchedImageXmlSerializer = new XmlSerializer(typeof(StitchedImage)); int startTileX; int startTileY; int endTileX; int endTileY; // Get the top left tile X & Y and the bottom right tile X & Y // We can work everything else out from this this.GetStartingAndEndTileXY(tileDownloadDirectory, out startTileX, out startTileY, out endTileX, out endTileY); //Debug.WriteLine("startTileX " + startTileX); //Debug.WriteLine("startTileY " + startTileY); //Debug.WriteLine("endTileX " + endTileX); //Debug.WriteLine("endTileY " + endTileY); int numberOfTilesX = (endTileX - startTileX) + 1; int numberOfTilesY = (endTileY - startTileY) + 1; //Debug.WriteLine("Tiles X " + numberOfTilesX); //Debug.WriteLine("Tiles Y " + numberOfTilesY); //Debug.WriteLine("Filename prefix " + filenamePrefix); var firstImageTile = this.LoadImageTile(tileDownloadDirectory, startTileX, startTileY); // Get some info from the first tile. We can assume all tiles have these // properties or something is very wrong var imageSource = firstImageTile.Source; var zoomLevel = firstImageTile.ZoomLevel; var tileWidth = firstImageTile.Width; var tileHeight = firstImageTile.Height; int maxTilesPerStitchedImageX = AeroSceneryManager.Instance.Settings.MaximumStitchedImageSize.Value; int maxTilesPerStitchedImageY = AeroSceneryManager.Instance.Settings.MaximumStitchedImageSize.Value; // Calculate the size of our stitched images in each direction var imageSizeX = maxTilesPerStitchedImageX * tileWidth; var imageSizeY = maxTilesPerStitchedImageY * tileHeight; // Calculate how many images we need var requiredStitchedImagesX = (int)Math.Ceiling((float)numberOfTilesX / (float)maxTilesPerStitchedImageX); var requiredStitchedImagesY = (int)Math.Ceiling((float)numberOfTilesY / (float)maxTilesPerStitchedImageY); var requiredStichedImages = requiredStitchedImagesX * requiredStitchedImagesY; int imageTileOffsetX = 0; int imagetileOffsetY = 0; tileStitcherProgress.TotalStitchedImages = requiredStichedImages; // Loop through each stitched image that we will need for (int stitchedImagesYIx = 0; stitchedImagesYIx < requiredStitchedImagesY; stitchedImagesYIx++) { for (int stitchedImagesXIx = 0; stitchedImagesXIx < requiredStitchedImagesX; stitchedImagesXIx++) { imageTileOffsetX = stitchedImagesXIx * maxTilesPerStitchedImageX; imagetileOffsetY = stitchedImagesYIx * maxTilesPerStitchedImageY; // This might not be right, but it's a reasonable estimate. We wont know until we read each file tileStitcherProgress.TotalImageTilesForCurrentStitchedImage = maxTilesPerStitchedImageX * maxTilesPerStitchedImageY; int columnsUsed = 0; int rowsUsed = 0; // By giving these incorrect values, we can be sure they will we overwritten without having // to make them nullable and do null checks double northLatitude = -500; double westLongitude = 500; double southLatitude = 500; double eastLongitude = -500; using (Bitmap bitmap = new System.Drawing.Bitmap(imageSizeX, imageSizeY)) { bitmap.MakeTransparent(); using (Graphics g = Graphics.FromImage(bitmap)) { tileStitcherProgress.CurrentTilesRenderedForCurrentStitchedImage = 0; // Work left to right, top to bottom // Loop through rows for (int yIx = 0; yIx < maxTilesPerStitchedImageY; yIx++) { bool rowHasImages = false; // Loop through columns for (int xIx = 0; xIx < maxTilesPerStitchedImageX; xIx++) { int currentTileX = xIx + imageTileOffsetX + startTileX; int currentTileY = yIx + imagetileOffsetY + startTileY; var imageTileData = this.LoadImageTile(tileDownloadDirectory, currentTileX, currentTileY); if (imageTileData != null) { // Even if all the images in this row are invalid, the aero files are present // so an attempt was made to download something rowHasImages = true; // Update our overall stitched image lat and long maxima and minima // We want the highest NorthLatitude value of any image tile for this stitched image if (imageTileData.NorthLatitude > northLatitude) { northLatitude = imageTileData.NorthLatitude; } // We want the lowest SouthLatitude value of any image tile for this stitched image if (imageTileData.SouthLatitude < southLatitude) { southLatitude = imageTileData.SouthLatitude; } // We want the lowest WestLongitude value of any image tile for this stitched image if (imageTileData.WestLongitude < westLongitude) { westLongitude = imageTileData.WestLongitude; } // We want the highest EastLongitude value of any image tile for this stitched image if (imageTileData.EastLongitude > eastLongitude) { eastLongitude = imageTileData.EastLongitude; } var imageTileFilename = tileDownloadDirectory + imageTileData.FileName + "." + imageTileData.ImageExtension; Image tile = null; try { tile = Image.FromFile(imageTileFilename); if (tile != null) { var imagePointX = (xIx * imageTileData.Width); var imagePointY = (yIx * imageTileData.Width); g.DrawImage(tile, new PointF(imagePointX, imagePointY)); tileStitcherProgress.CurrentTilesRenderedForCurrentStitchedImage++; progress.Report(tileStitcherProgress); } tile.Dispose(); } catch (Exception) { // The image file was probably invalid, but there's not a lot we can do // Leave it transparent } finally { // Even if the image was invalid, we still had an aero file for it // so it counts as a used column var colsUsedInThisRow = xIx + 1; if (columnsUsed < colsUsedInThisRow) { columnsUsed = colsUsedInThisRow; } if (tile != null) { tile.Dispose(); } } } } if (rowHasImages) { rowsUsed++; } } } var stitchFilename = String.Format("{0}_{1}_stitch_{2}_{3}.png", imageSource, zoomLevel, stitchedImagesXIx + 1, stitchedImagesYIx + 1); var stitchedImage = new StitchedImage(); stitchedImage.ImageExtension = "png"; stitchedImage.NorthLatitude = northLatitude; stitchedImage.WestLongitude = westLongitude; stitchedImage.SouthLatitude = southLatitude; stitchedImage.EastLongitude = eastLongitude; stitchedImage.Width = columnsUsed * tileWidth; stitchedImage.Height = rowsUsed * tileHeight; stitchedImage.Source = imageSource; stitchedImage.ZoomLevel = zoomLevel; stitchedImage.StichedImageSetX = stitchedImagesXIx + 1; stitchedImage.StichedImageSetY = stitchedImagesYIx + 1; //Debug.WriteLine("Rows Used " + rowsUsed); //Debug.WriteLine("Columns Used " + columnsUsed); // Have we drawn an image to the maximum number of rows and columns for this image? if (columnsUsed == maxTilesPerStitchedImageX && rowsUsed == maxTilesPerStitchedImageY) { // Save the bitmap as it is log.InfoFormat("Saving stitched image {0}", stitchFilename); bitmap.Save(stitchedTilesDirectory + stitchFilename, ImageFormat.Png); tileStitcherProgress.CurrentStitchedImage++; } else { // Resize the bitmap down to the used number of rows and columns log.InfoFormat("Cropping stitched image {0}", stitchFilename); CropBitmap(bitmap, new Rectangle(0, 0, columnsUsed *tileWidth, rowsUsed *tileHeight), stitchedTilesDirectory, stitchFilename); tileStitcherProgress.CurrentStitchedImage++; } this.SaveStitchedImageAeroFile(this.stitchedImageXmlSerializer, stitchedImage, stitchedTilesDirectory); } } } if (deleteOriginals) { } }); }