diff --git a/EarthLiveSharp/EarthLiveSharp.csproj b/EarthLiveSharp/EarthLiveSharp.csproj index f19638e..7bd45f9 100644 --- a/EarthLiveSharp/EarthLiveSharp.csproj +++ b/EarthLiveSharp/EarthLiveSharp.csproj @@ -1,155 +1,49 @@  - - + + - Debug - AnyCPU - {5916F0E1-8196-4229-97C8-7DA0AA91AF0E} WinExe - Properties - EarthLiveSharp - EarthLiveSharp - v4.5 - 512 - false - - 发布\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - EarthLive.ico + net48 + true + false + true + + true + - - - - - + + + + + - - - Form - - - settingsForm.cs - - - Form - - - mainForm.cs - - - - - - mainForm.cs - - - mainForm.cs - - - settingsForm.cs - - - mainForm.cs - - + + + + + ResXFileCodeGenerator Resources.Designer.cs - Designer - + + True True Resources.resx - True - - settingsForm.cs - - - settingsForm.cs - - + + SettingsSingleFileGenerator Settings.Designer.cs - - + + + True True Settings.settings - True - - - Designer - - - - - - - - - - - False - Microsoft .NET Framework 4.5 %28x86 和 x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - - - + \ No newline at end of file diff --git a/EarthLiveSharp/Program.cs b/EarthLiveSharp/Program.cs index 893e7ab..defff58 100644 --- a/EarthLiveSharp/Program.cs +++ b/EarthLiveSharp/Program.cs @@ -1,14 +1,30 @@ using System; using System.Windows.Forms; using System.Net; -using System.Net.Cache; +using System.Net.Cache; using System.IO; using System.Diagnostics; using Microsoft.Win32; using System.Drawing; +using System.Drawing.Drawing2D; +using System.Collections.Generic; namespace EarthLiveSharp { + // Helper struct for City Data + public struct City + { + public string Name; + public double Lat; + public double Lon; + public City(string name, double lat, double lon) + { + Name = name; + Lat = lat; + Lon = lon; + } + } + static class Program { [System.Runtime.InteropServices.DllImport("user32.dll")] @@ -36,7 +52,7 @@ static void Main(string[] args) { return; } - if (Cfg.source_selection ==0 & Cfg.cloud_name.Equals("demo")) + if (Cfg.source_selection == 0 & Cfg.cloud_name.Equals("demo")) { #if DEBUG @@ -48,10 +64,7 @@ static void Main(string[] args) } #endif } - //if (Cfg.language.Equals("en")| Cfg.language.Equals("zh-Hans")| Cfg.language.Equals("zh-Hant")) - //{ - // System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(Cfg.language); - //} + Cfg.image_folder = Application.StartupPath + @"\images"; Cfg.Save(); Scrap_wrapper.set_scraper(); @@ -92,12 +105,24 @@ interface IScraper void CleanCDN(); void ResetState(); } - public class Scraper_himawari8 : IScraper + +public class Scraper_himawari8 : IScraper { private string imageID = ""; private static string last_imageID = "0"; private string json_url = "https://himawari8-dl.nict.go.jp/himawari8/img/FULL_24h/latest.json"; + // Cities List + private List cities = new List() + { + new City("Shanghai", 31.23, 121.47), + new City("Sydney", -33.86, 151.21), + new City("Adelaide", -34.93, 138.60), + new City("Perth", -31.95, 115.86), + new City("Brisbane", -27.47, 153.03), + new City("Melbourne", -37.81, 144.96) + }; + private int GetImageID() { System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; @@ -149,7 +174,7 @@ private int SaveImage() for (int jj = 0; jj < Cfg.size; jj++) { url = string.Format("{0}/{1}d/550/{2}_{3}_{4}.png", image_source, Cfg.size, imageID, ii, jj); - image_path = string.Format("{0}\\{1}_{2}.png", Cfg.image_folder, ii, jj); // remove the '/' in imageID + image_path = string.Format("{0}\\{1}_{2}.png", Cfg.image_folder, ii, jj); client.DownloadFile(url, image_path); } } @@ -168,100 +193,214 @@ private int SaveImage() } } - private void JoinImage() + // --- FIXED PROJECTION GEOMETRY --- + private Point GetPixelCoordinates(double lat, double lon, int width, int height) { - // join & convert the images to wallpaper.bmp - Bitmap bitmap = new Bitmap(550 * Cfg.size, 550 * Cfg.size); - Image[,] tile = new Image[Cfg.size, Cfg.size]; - Graphics g = Graphics.FromImage(bitmap); - for (int ii = 0; ii < Cfg.size; ii++) - { - for (int jj = 0; jj < Cfg.size; jj++) - { - tile[ii,jj] = Image.FromFile(string.Format("{0}\\{1}_{2}.png", Cfg.image_folder, ii, jj)); - g.DrawImage(tile[ii, jj], 550 * ii, 550 * jj); - tile[ii, jj].Dispose(); - } - } - g.Save(); - g.Dispose(); - if (Cfg.zoom == 100) - { - bitmap.Save(string.Format("{0}\\wallpaper.bmp", Cfg.image_folder),System.Drawing.Imaging.ImageFormat.Bmp); - } - else if (1 < Cfg.zoom & Cfg.zoom <100) - { - int new_size = bitmap.Height * Cfg.zoom /100; - Bitmap zoom_bitmap = new Bitmap(new_size, new_size); - Graphics g_2 = Graphics.FromImage(zoom_bitmap); - g_2.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; - g_2.DrawImage(bitmap, 0, 0, new_size, new_size); - g_2.Save(); - g_2.Dispose(); - zoom_bitmap.Save(string.Format("{0}\\wallpaper.bmp", Cfg.image_folder),System.Drawing.Imaging.ImageFormat.Bmp); - zoom_bitmap.Dispose(); - } - else + // Himawari-8/9 Sub-satellite Point + double satLon = 140.7; + + double radLat = lat * Math.PI / 180.0; + double radLon = lon * Math.PI / 180.0; + double radSatLon = satLon * Math.PI / 180.0; + double radLonDiff = radLon - radSatLon; + + // Perspective constants + // P = distance_satellite / radius_earth = 42164 / 6378 + double P = 6.610689; + + // Geocentric coords + double x_g = Math.Cos(radLat) * Math.Cos(radLonDiff); + double y_g = Math.Cos(radLat) * Math.Sin(radLonDiff); + double z_g = Math.Sin(radLat); + + // Visibility check (hide if on the back side) + if (x_g < 0.155) // approximate limb limit { - Trace.WriteLine("[himawari8 zoom error]"); + return new Point(-100, -100); } - bitmap.Dispose(); + // Vertical Perspective Projection (View from P looking at 0) + // The mapping factor K maps the geometric projection to the normalized plane + double K = (P - 1) / (P - x_g); + + double x_proj = K * y_g; + double y_proj = K * z_g; - if (Cfg.saveTexture && Cfg.saveDirectory != "selected Directory") + // CALIBRATION FIX: + // At the limb (edge of earth), x_proj is approx 0.858. + // We want the limb to be at the edge of the image (0.5 coordinates). + // Scale = 0.5 / 0.858 = ~0.582 + double scale = 0.582; + + int pixelX = (int)((0.5 + x_proj * scale) * width); + int pixelY = (int)((0.5 - y_proj * scale) * height); + + return new Point(pixelX, pixelY); + } + + private void DrawCityMarkers(Bitmap bmp) + { + // Dynamic Sizing + int refSize = bmp.Width; + int fontSize = Math.Max(14, refSize / 65); + int dotSize = Math.Max(8, refSize / 180); + int padding = dotSize / 2; + + using (Graphics g = Graphics.FromImage(bmp)) { - if (Scrap_wrapper.SequenceCount >= Cfg.saveMaxCount) + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; + + using (Brush brushRed = new SolidBrush(Color.Red)) + using (Pen penWhite = new Pen(Color.White, 2)) + using (Font font = new Font("Arial", fontSize, FontStyle.Bold)) + using (GraphicsPath path = new GraphicsPath()) { - Scrap_wrapper.SequenceCount = 0; + foreach (var city in cities) + { + Point p = GetPixelCoordinates(city.Lat, city.Lon, bmp.Width, bmp.Height); + + // Skip if off-screen (e.g. hidden side) + if (p.X < 0 || p.Y < 0) continue; + + // Draw Dot + Rectangle dotRect = new Rectangle(p.X - padding, p.Y - padding, dotSize, dotSize); + g.FillEllipse(brushRed, dotRect); + g.DrawEllipse(penWhite, dotRect); + + // Draw Text with Outline + string text = city.Name; + PointF textPos = new PointF(p.X + dotSize, p.Y - (fontSize / 2)); + + path.Reset(); + path.AddString(text, font.FontFamily, (int)font.Style, font.Size, textPos, StringFormat.GenericDefault); + + g.DrawPath(new Pen(Color.Black, 3), path); + g.FillPath(Brushes.White, path); + } } - try + } + } + + private void JoinImage() + { + int fullWidth = 550 * Cfg.size; + int fullHeight = 550 * Cfg.size; + + using (Bitmap fullDisk = new Bitmap(fullWidth, fullHeight)) + { + // 1. Reconstruct Full Disk + using (Graphics g = Graphics.FromImage(fullDisk)) { - File.Copy(string.Format("{0}\\wallpaper.bmp", Cfg.image_folder), Cfg.saveDirectory + "\\" + "wallpaper_" + Scrap_wrapper.SequenceCount + ".bmp", true); - Scrap_wrapper.SequenceCount++; + for (int ii = 0; ii < Cfg.size; ii++) + { + for (int jj = 0; jj < Cfg.size; jj++) + { + string tilePath = string.Format("{0}\\{1}_{2}.png", Cfg.image_folder, ii, jj); + if (File.Exists(tilePath)) + { + using (Image tile = Image.FromFile(tilePath)) + { + g.DrawImage(tile, 550 * ii, 550 * jj); + } + } + } + } } - catch (Exception e) + + // 2. MARK CITIES on the Full Disk + DrawCityMarkers(fullDisk); + + // 3. Prepare Crops + // Crop A: Australia (Main) + int auX = (int)(fullWidth * 0.25); + int auY = (int)(fullHeight * 0.55); + int auW = (int)(fullWidth * 0.45); + int auH = (int)(fullHeight * 0.35); + if (auX + auW > fullWidth) auW = fullWidth - auX; + if (auY + auH > fullHeight) auH = fullHeight - auY; + Rectangle rectAu = new Rectangle(auX, auY, auW, auH); + + // Crop B: Shanghai Area (Inset) + int shX = (int)(fullWidth * 0.15); + int shY = (int)(fullHeight * 0.15); + int shW = (int)(fullWidth * 0.30); + int shH = (int)(fullHeight * 0.30); + if (shX + shW > fullWidth) shW = fullWidth - shX; + if (shY + shH > fullHeight) shH = fullHeight - shY; + Rectangle rectSh = new Rectangle(shX, shY, shW, shH); + + // 4. Composition + using (Bitmap wallpaper = fullDisk.Clone(rectAu, fullDisk.PixelFormat)) { - Trace.WriteLine("[can't save wallpaper to distDirectory]"); - Trace.WriteLine(e.Message); - return; + using (Bitmap shanghaiImg = fullDisk.Clone(rectSh, fullDisk.PixelFormat)) + { + // Resize Shanghai Inset to 35% of wallpaper width + int insetWidth = (int)(wallpaper.Width * 0.35); + int insetHeight = (int)((double)shanghaiImg.Height / shanghaiImg.Width * insetWidth); + + using (Bitmap shanghaiSmall = new Bitmap(insetWidth, insetHeight)) + { + using (Graphics gSmall = Graphics.FromImage(shanghaiSmall)) + { + gSmall.InterpolationMode = InterpolationMode.HighQualityBicubic; + gSmall.DrawImage(shanghaiImg, 0, 0, insetWidth, insetHeight); + } + + // Draw Inset + using (Graphics gFinal = Graphics.FromImage(wallpaper)) + { + int padding = 20; + // White border around inset + gFinal.DrawRectangle(new Pen(Color.White, 3), padding, padding, insetWidth, insetHeight); + gFinal.DrawImage(shanghaiSmall, padding, padding, insetWidth, insetHeight); + } + } + } + + // Save + string wallpaperPath = string.Format("{0}\\wallpaper.bmp", Cfg.image_folder); + wallpaper.Save(wallpaperPath, System.Drawing.Imaging.ImageFormat.Bmp); + + if (Cfg.saveTexture && Cfg.saveDirectory != "selected Directory") + { + if (Scrap_wrapper.SequenceCount >= Cfg.saveMaxCount) + { + Scrap_wrapper.SequenceCount = 0; + } + try + { + string archivePath = Cfg.saveDirectory + "\\" + "wallpaper_" + Scrap_wrapper.SequenceCount + ".bmp"; + wallpaper.Save(archivePath, System.Drawing.Imaging.ImageFormat.Bmp); + Scrap_wrapper.SequenceCount++; + } + catch (Exception e) + { + Trace.WriteLine("[can't save wallpaper]"); + Trace.WriteLine(e.Message); + } + } } } } private void InitFolder() { - if(Directory.Exists(Cfg.image_folder)) - { - // delete all images in the image folder. - //string[] files = Directory.GetFiles(image_folder); - //foreach (string fn in files) - //{ - // File.Delete(fn); - //} - } - else + if(!Directory.Exists(Cfg.image_folder)) { - Trace.WriteLine("[himawari8 create folder]"); Directory.CreateDirectory(Cfg.image_folder); } } public void UpdateImage() { InitFolder(); - if (GetImageID() == -1) - { - return; - } - if (imageID.Equals(last_imageID)) - { - return; - } + if (GetImageID() == -1) return; + if (imageID.Equals(last_imageID)) return; if (SaveImage()==0) { JoinImage(); } last_imageID = imageID; - return; } public void CleanCDN() { @@ -278,47 +417,22 @@ public void CleanCDN() HttpWebResponse response = null; StreamReader reader = null; string result = null; - for (int i = 0; i < 3;i++ ) // max 3 request each hour. + for (int i = 0; i < 3;i++ ) { response = request.GetResponse() as HttpWebResponse; - if (response.StatusCode != HttpStatusCode.OK) - { - throw new Exception("[himawari8 clean CND cache connection error]"); - } - if (!response.ContentType.Contains("application/json")) - { - throw new Exception("[himawari8 clean CND cache no json recieved. your Internet connection is hijacked]"); - } + if (response.StatusCode != HttpStatusCode.OK) throw new Exception("Error"); reader = new StreamReader(response.GetResponseStream()); result = reader.ReadToEnd(); - if (result.Contains("\"error\"")) - { - throw new Exception("[himawari8 clean CND cache request error]\n" + result); - } - if (result.Contains("\"partial\":false")) - { - Trace.WriteLine("[himawari8 clean CDN cache done]"); - break; // end of Clean CDN - } - else - { - Trace.WriteLine("[himawari8 more images to delete]"); - } + if (result.Contains("\"partial\":false")) break; } } - catch (Exception e) - { - Trace.WriteLine("[himawari8 error when delete CDN cache]"); - Trace.WriteLine(e.Message); - return; - } + catch (Exception e) { Trace.WriteLine(e.Message); } } public void ResetState() { last_imageID = "0"; } } - public static class Autostart { static string key = "EarthLiveSharp"; @@ -335,7 +449,7 @@ public static bool Set(bool enabled) } else { - runKey.SetValue(key, path); // dirty fix: to avoid exception in next line. + runKey.SetValue(key, path); runKey.DeleteValue(key); } return true; @@ -354,4 +468,4 @@ public static bool Set(bool enabled) } } } -} +} \ No newline at end of file