Code snippet showing how to create an equirectangular cloud image from University of Wisconsin-Madison Space Science and Engineering Center web mercator tiles, see https://ithoughthecamewithyou.com/post/improving-the-accuracy-of-the-new-catfood-earth-clouds-layer for more information
string cloudsDownloadPath = HostingEnvironment.MapPath(CloudsDownloadName); | |
string cloudsTransformedPath = HostingEnvironment.MapPath(CloudsTransformedName); | |
string cloudsWarpedPath = HostingEnvironment.MapPath(CloudsWarpedName); | |
string cloudsFinalPath = HostingEnvironment.MapPath(AxdCloudsHandler.CloudsPath); | |
string gdalFolder = HostingEnvironment.MapPath(GdalFolder); | |
string gdalTranslatePath = HostingEnvironment.MapPath(GdalTranslate); | |
string gdalWarpPath = HostingEnvironment.MapPath(GdalWarp); | |
// download the most recent tiles | |
using (Bitmap cloudImage = new Bitmap(CloudsTileDimension * CloudsTileCount, CloudsTileDimension * CloudsTileCount, PixelFormat.Format32bppArgb)) | |
{ | |
using (Graphics g = Graphics.FromImage(cloudImage)) | |
{ | |
g.CompositingMode = CompositingMode.SourceCopy; | |
g.CompositingQuality = CompositingQuality.HighQuality; | |
g.InterpolationMode = InterpolationMode.HighQualityBicubic; | |
g.SmoothingMode = SmoothingMode.HighQuality; | |
g.PixelOffsetMode = PixelOffsetMode.HighQuality; | |
using (WebClient webClient = new WebClient()) | |
{ | |
for (int col = 0; col < CloudsTileCount; col++) | |
{ | |
for (int row = 0; row < CloudsTileCount; row++) | |
{ | |
using (Stream tileStream = webClient.OpenRead($"http://realearth.ssec.wisc.edu/products/globalir/4/{col}/{row}.png")) | |
{ | |
// for future expansion, might be better to project onto a cylinder... | |
using (Bitmap tile = new Bitmap(tileStream)) | |
{ | |
int x = col * CloudsTileDimension; | |
int y = row * CloudsTileDimension; | |
g.DrawImage(tile, x, y); | |
} | |
} | |
} | |
} | |
} | |
} | |
cloudImage.Save(cloudsDownloadPath, ImageFormat.Png); | |
} | |
// add dimensions to image | |
ProcessStartInfo psiTranslate = new ProcessStartInfo(); | |
psiTranslate.Arguments = $"-a_srs EPSG:3857 -a_ullr -20037508 20037508 20037508 -20037508 -r lanczos {cloudsDownloadPath} {cloudsTransformedPath}"; | |
psiTranslate.CreateNoWindow = true; | |
psiTranslate.UseShellExecute = false; | |
psiTranslate.FileName = gdalTranslatePath; | |
psiTranslate.LoadUserProfile = false; | |
psiTranslate.WorkingDirectory = gdalFolder; | |
Process pTranslate = Process.Start(psiTranslate); | |
pTranslate.WaitForExit(); | |
// warp image | |
ProcessStartInfo psiWarp = new ProcessStartInfo(); | |
psiWarp.Arguments = $"-s_srs EPSG:3857 -t_srs EPSG:4326 -r lanczos -wo SOURCE_EXTRA=1000 -overwrite -te -180 -88.99999 180 88.99999 -te_srs EPSG:4326 {cloudsTransformedPath} {cloudsWarpedPath}"; | |
psiWarp.CreateNoWindow = true; | |
psiWarp.UseShellExecute = false; | |
psiWarp.FileName = gdalWarpPath; | |
psiWarp.LoadUserProfile = false; | |
psiWarp.WorkingDirectory = gdalFolder; | |
Process pWarp = Process.Start(psiWarp); | |
pWarp.WaitForExit(); | |
// drop lighter pixels and flip | |
using (Image warped = Image.FromFile(cloudsWarpedPath)) | |
{ | |
using (Bitmap final = new Bitmap(CloudsFinalWidth, CloudsFinalHeight, PixelFormat.Format32bppArgb)) | |
{ | |
using (Graphics g = Graphics.FromImage(final)) | |
{ | |
g.CompositingMode = CompositingMode.SourceCopy; | |
g.CompositingQuality = CompositingQuality.HighQuality; | |
g.InterpolationMode = InterpolationMode.HighQualityBicubic; | |
g.SmoothingMode = SmoothingMode.HighQuality; | |
g.PixelOffsetMode = PixelOffsetMode.HighQuality; | |
g.FillRectangle(Brushes.Black, 0, 0, CloudsFinalWidth, CloudsFinalHeight); | |
g.DrawImage(warped, | |
new Rectangle(0, 0, CloudsFinalWidth, CloudsFinalHeight), | |
0, 0, warped.Width, warped.Height, | |
GraphicsUnit.Pixel); | |
// need to fill in missing area at top and bottom | |
FlipClouds(final, g, true); | |
FlipClouds(final, g, false); | |
} | |
for (int x = 0; x < final.Width; x++) | |
{ | |
for (int y = 0; y < final.Height; y++) | |
{ | |
Color c = final.GetPixel(x, y); | |
final.SetPixel(x, y, Color.FromArgb( | |
(int)Math.Max(c.R - ((255.0 - c.R) * CloudsPixelFactor), 0), | |
(int)Math.Max(c.G - ((255.0 - c.G) * CloudsPixelFactor), 0), | |
(int)Math.Max(c.B - ((255.0 - c.B) * CloudsPixelFactor), 0) | |
)); | |
} | |
} | |
lock (AxdCloudsHandler.CloudsUpdateLock) | |
{ | |
final.Save(cloudsFinalPath, ImageFormat.Jpeg); | |
} | |
} | |
} | |
// delete intermediate | |
if (File.Exists(cloudsWarpedPath)) { File.Delete(cloudsWarpedPath); } | |
if (File.Exists(cloudsTransformedPath)) { File.Delete(cloudsTransformedPath); } | |
if (File.Exists(cloudsDownloadPath)) { File.Delete(cloudsDownloadPath); } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment