Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Adjuvant/a94b538463f74bcae0fe565ffc75e93d to your computer and use it in GitHub Desktop.

Select an option

Save Adjuvant/a94b538463f74bcae0fe565ffc75e93d to your computer and use it in GitHub Desktop.
Getting around NASA Earth data server changes to download SRTM30 heighmap data via Real World Terrain plugin, probably unrecognisable to original. Unity 2022.3.62f3 on Windows 11 worked.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using InfinityCode.RealWorldTerrain.Net;
using InfinityCode.RealWorldTerrain.Phases;
using InfinityCode.RealWorldTerrain.Windows;
using UnityEngine;
using UnityEngine.Networking;
namespace InfinityCode.RealWorldTerrain.Generators
{
public class RealWorldTerrainSRTM30ElevationGenerator : RealWorldTerrainElevationGenerator
{
private const string server = "https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/SRTMGL1.003/";
// FIXED: Removed 'static' to prevent adjacent tiles from stealing each other's URLs
private List<RealWorldTerrainDownloadItemWebClient> downloaderItems;
public static string login;
public static string pass;
private readonly string filename;
private readonly string arcFilename;
private readonly string heightmapFilename;
private readonly int x1, y1, x2, y2;
private double mx1, mx2, my1, my2;
private static object exclusiveLock;
public RealWorldTerrainSRTM30ElevationGenerator(int X, int Y) // Removed 'ref needAuth'
{
x1 = X; x2 = X + 1; y1 = Y + 1; y2 = Y;
mapSize = 3601; mapSize2 = mapSize - 1;
RealWorldTerrainGeo.LatLongToMercat(x1, y1, out mx1, out my1);
RealWorldTerrainGeo.LatLongToMercat(x2, y2, out mx2, out my2);
string ax = (x1 < 0 ? "W" : "E") + Math.Abs(x1).ToString("D3");
string ay = (y2 < 0 ? "S" : "N") + Math.Abs(y2).ToString("D2");
string tileName = $"{ay}{ax}";
filename = $"{tileName}.hgt";
arcFilename = Path.Combine(RealWorldTerrainEditorUtils.heightmapCacheFolder, filename + ".zip");
heightmapFilename = Path.Combine(RealWorldTerrainEditorUtils.heightmapCacheFolder, filename);
string url = $"{server}{tileName}.SRTMGL1.hgt/{tileName}.SRTMGL1.hgt.zip";
if (exclusiveLock == null) exclusiveLock = new object();
if (!File.Exists(heightmapFilename) && !File.Exists(arcFilename))
{
// FORCE AUTH FOR EVERY TILE
RealWorldTerrainDownloadItemAction authItem = new RealWorldTerrainDownloadItemAction { exclusiveLock = exclusiveLock };
authItem["url"] = url;
authItem["step"] = 1;
authItem["tileName"] = tileName;
authItem.OnStart = OnAuthStep;
authItem.OnCheckComplete = OnAuthCheckComplete;
downloaderItems = new List<RealWorldTerrainDownloadItemWebClient>();
RealWorldTerrainDownloadItemWebClient item = new RealWorldTerrainDownloadItemWebClient(url)
{
filename = arcFilename,
averageSize = 11000000,
exclusiveLock = exclusiveLock
};
downloaderItems.Add(item);
item.OnError += OnDownloadError;
}
}
private void OnAuthStep(RealWorldTerrainDownloadItemAction action)
{
string url = action.GetField<string>("url");
UnityWebRequest uwr = UnityWebRequest.Get(url);
Debug.Log($"<color=cyan>[RWT Auth]</color> Requesting: {url}");
string auth = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{login.Trim()}:{pass.Trim()}"));
uwr.SetRequestHeader("Authorization", "Basic " + auth);
uwr.redirectLimit = 10;
action["uwr"] = uwr;
uwr.SendWebRequest();
}
private void OnAuthCheckComplete(RealWorldTerrainDownloadItemAction action)
{
UnityWebRequest uwr = action.GetField<UnityWebRequest>("uwr");
if (!uwr.isDone) return;
Debug.Log($"<color=orange>[RWT Auth Result]</color> Status: {uwr.responseCode} | Final URL: {uwr.url}");
if (uwr.result == UnityWebRequest.Result.Success && (uwr.responseCode == 200 || uwr.responseCode == 302))
{
Dictionary<string, string> headers = uwr.GetResponseHeaders();
foreach (RealWorldTerrainDownloadItemWebClient item in downloaderItems)
{
item.url = uwr.url;
item.headers = new Dictionary<string, string>();
if (headers != null && headers.ContainsKey("Set-Cookie"))
item.headers.Add("Cookie", headers["Set-Cookie"]);
}
action.Dispose();
action.complete = true;
}
else
{
Debug.LogError($"[RWT] Auth Failed: {uwr.error}");
RealWorldTerrainWindow.CancelCapture();
}
}
public void ParseHeightmap()
{
try
{
heightmap = new short[mapSize, mapSize];
if (!File.Exists(heightmapFilename))
{
Debug.LogWarning($"[RWT] Parse failed: {heightmapFilename} not found.");
if (!prefs.ignoreSRTMErrors) RealWorldTerrainWindow.CancelInMainThread();
return;
}
using (FileStream fs = File.Open(heightmapFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
int c = 0;
long totalLength = fs.Length;
byte[] buffer = new byte[1024 * 1024];
while (fs.Position < totalLength)
{
// ELEGANT EXIT: Master kill-switch check
if (!RealWorldTerrainWindow.isCapturing)
{
Debug.LogWarning("[RWT] Parsing aborted by system.");
return;
}
int count = fs.Read(buffer, 0, buffer.Length);
for (int i = 0; i < count; i += 2)
{
short s = (short)(buffer[i] * 256 + buffer[i + 1]);
if (s == 0) s = short.MinValue;
heightmap[c % mapSize, c / mapSize] = s;
c++;
}
if (c % 100000 == 0)
{
RealWorldTerrainPhase.phaseProgress = (float)fs.Position / totalLength;
}
}
}
Debug.Log($"<color=green>[RWT]</color> Successfully parsed heightmap: {filename}");
}
catch (Exception e)
{
Debug.LogError($"[RWT Parse Error] {e.Message}");
RealWorldTerrainWindow.CancelInMainThread();
}
finally
{
// ALWAYS clean up memory, even on a crash
GC.Collect();
}
}
public override void UnzipHeightmap()
{
try
{
if (File.Exists(heightmapFilename)) { unziped = true; return; }
if (!File.Exists(arcFilename)) { return; }
Debug.Log($"[RWT] Attempting Unzip: {arcFilename}");
int retries = 5;
while (retries > 0)
{
// ELEGANT EXIT
if (!RealWorldTerrainWindow.isCapturing) return;
try
{
using (FileStream fs = File.Open(arcFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
using (ZipArchive zip = new ZipArchive(fs, ZipArchiveMode.Read))
{
foreach (ZipArchiveEntry entry in zip.Entries)
{
// ELEGANT EXIT
if (!RealWorldTerrainWindow.isCapturing) return;
Debug.Log($"[RWT Zip Content] Found: {entry.Name} (Looking for: {filename})");
if (entry.Name.IndexOf(filename, StringComparison.OrdinalIgnoreCase) >= 0)
{
entry.ExtractToFile(heightmapFilename, true);
unziped = true;
Debug.Log($"<color=green>[RWT]</color> Unzipped {entry.Name} successfully.");
return;
}
}
}
// If loop finishes without finding the file
Debug.LogError($"[RWT Unzip Failed] Could not find {filename} inside the archive.");
if (!prefs.ignoreSRTMErrors) RealWorldTerrainWindow.CancelInMainThread();
return;
}
catch (IOException)
{
// File locked. Do a breakable micro-sleep instead of a hard Thread.Sleep
for (int s = 0; s < 50; s++)
{
if (!RealWorldTerrainWindow.isCapturing) return; // Break out immediately if cancelled
System.Threading.Thread.Sleep(10);
}
retries--;
}
}
Debug.LogError($"[RWT] Unzip timed out due to file locks on {filename}.");
}
catch (Exception e)
{
Debug.LogError($"[RWT Unzip Error] {e.Message}");
if (!prefs.ignoreSRTMErrors) RealWorldTerrainWindow.CancelInMainThread();
}
}
public override bool Contains(double x, double y) { return x >= mx1 && x <= mx2 && y >= my1 && y <= my2; }
private void OnDownloadError(RealWorldTerrainDownloadItem item) { if (!prefs.ignoreSRTMErrors) RealWorldTerrainWindow.CancelInMainThread(); }
public new static void Dispose()
{
if (elevations != null) { foreach (var e in elevations) e.heightmap = null; elevations = null; }
lastX = 0; lastElevation = null;
}
public override double GetElevationValue(double x, double y)
{
RealWorldTerrainGeo.MercatToLatLong(x, y, out x, out y);
x -= x1; y = y1 - y;
double cx = x * mapSize2, cy = y * mapSize2;
int ix = (int)cx, iy = (int)cy;
if (prefs.nodataValue != 0 && GetValue(ix, iy) == short.MinValue) return double.MinValue;
double ox = cx - ix, oy = cy - iy;
return GetSmoothElevation(ox - 1, ox - 2, ox + 1, oy - 1, oy - 2, oy + 1, ix, iy, ox, ix + 1, oy, iy + 1, ox * oy, ix - 1, iy - 1, ix + 2, iy + 2);
}
protected override short GetValue(int x, int y)
{
if (x < 0) x = 0; else if (x > mapSize2) x = mapSize2;
if (y < 0) y = 0; else if (y > mapSize2) y = mapSize2;
return heightmap[x, y];
}
public static void GetSRTMElevationRange(out double minEl, out double maxEl)
{
minEl = double.MaxValue;
maxEl = double.MinValue;
int cx = prefs.terrainCount.x * (prefs.heightmapResolution - 1) + 1;
int cy = prefs.terrainCount.y * (prefs.heightmapResolution - 1) + 1;
const int maxV = 4097;
if (cx > maxV && cx > cy)
{
float sv = maxV / (float)cx;
cx = maxV;
cy = Mathf.RoundToInt(cy * sv);
}
else if (cy > maxV)
{
float sv = maxV / (float)cy;
cy = maxV;
cx = Mathf.RoundToInt(cx * sv);
}
double rx = (prefs.rightLongitude - prefs.leftLongitude) / cx;
double ry = (prefs.topLatitude - prefs.bottomLatitude) / cy;
for (int x = 0; x < cx; x++)
{
double tx = x * rx + prefs.leftLongitude;
for (int y = 0; y < cy; y++)
{
double ty = y * ry + prefs.bottomLatitude;
double mx, my;
RealWorldTerrainGeo.LatLongToMercat(tx, ty, out mx, out my);
double el = GetElevation(mx, my);
if (Math.Abs(el - double.MinValue) > double.Epsilon)
{
if (el < minEl) minEl = el;
if (el > maxEl) maxEl = el;
}
}
}
if (minEl > prefs.nodataValue) minEl = prefs.nodataValue;
}
public static void Init()
{
int sx = (int)Math.Floor(prefs.leftLongitude), sy = (int)Math.Floor(prefs.topLatitude);
int ex = (int)Math.Floor(prefs.rightLongitude), ey = (int)Math.Floor(prefs.bottomLatitude);
for (int x = sx; x <= ex; x++) {
for (int y = sy; y >= ey; y--) {
// No longer passing 'ref needAuth'
elevations.Add(new RealWorldTerrainSRTM30ElevationGenerator(x, y));
}
}
double rangeX = prefs.rightLongitude - prefs.leftLongitude;
depthStep = (float)(Math.Abs(prefs.depthSharpness) * rangeX / (prefs.heightmapResolution - 1) / prefs.terrainCount.x) * 100;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment