qualia_client_core/chora/layers/
nasa_gibs.rs1pub struct GibsRequest {
2 pub layer: String,
3 pub projection: String,
4 pub width: u32,
5 pub height: u32,
6}
7
8impl GibsRequest {
9 pub fn url(&self) -> String {
10 let bbox = if self.projection == "epsg4326" {
11 "-90,-180,90,180"
12 } else {
13 "-20037508.34,-20037508.34,20037508.34,20037508.34"
14 };
15 let crs = if self.projection == "epsg4326" {
16 "EPSG:4326"
17 } else {
18 "EPSG:3857"
19 };
20 format!(
21 "https://gibs.earthdata.nasa.gov/wms/{}/best/wms.cgi?\
22 SERVICE=WMS&REQUEST=GetMap&VERSION=1.3.0\
23 &LAYERS={}&CRS={}&BBOX={}\
24 &WIDTH={}&HEIGHT={}&FORMAT=image/jpeg\
25 &STYLES=&TRANSPARENT=FALSE",
26 self.projection, self.layer, crs, bbox, self.width, self.height
27 )
28 }
29}
30
31pub struct EarthTexture {
32 pub width: u32,
33 pub height: u32,
34 pub rgb: Vec<[u8; 3]>,
35}
36
37impl EarthTexture {
38 pub fn sample(&self, lat_deg: f32, lon_deg: f32) -> [f32; 3] {
39 let lat = lat_deg.clamp(-90.0, 90.0);
40 let lon = lon_deg.clamp(-180.0, 180.0);
41 let v = ((90.0 - lat) / 180.0 * self.height as f32) as u32;
42 let u = (((lon + 180.0) / 360.0) * self.width as f32) as u32;
43 let v = v.min(self.height - 1);
44 let u = u.min(self.width - 1);
45 let idx = (v * self.width + u) as usize;
46 let [r, g, b] = self.rgb.get(idx).copied().unwrap_or([0, 0, 80]);
47 [r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0]
48 }
49
50 pub fn sample_vertex(&self, x: f32, y: f32, z: f32) -> [f32; 3] {
51 let lat = y.asin().to_degrees();
52 let lon = z.atan2(x).to_degrees();
53 self.sample(lat, lon)
54 }
55}
56
57pub fn decode_jpeg_rgb(data: &[u8]) -> Result<EarthTexture, String> {
58 let img = image::load_from_memory_with_format(data, image::ImageFormat::Jpeg)
59 .map_err(|e| format!("JPEG decode: {e}"))?;
60 let rgb_img = img.to_rgb8();
61 let width = rgb_img.width();
62 let height = rgb_img.height();
63 let mut rgb = Vec::with_capacity((width * height) as usize);
64 for pixel in rgb_img.pixels() {
65 rgb.push([pixel[0], pixel[1], pixel[2]]);
66 }
67 Ok(EarthTexture { width, height, rgb })
68}
69
70pub async fn download_gibs_texture(req: &GibsRequest) -> Result<EarthTexture, String> {
71 let url = req.url();
72 let response = reqwest::get(&url)
73 .await
74 .map_err(|e| format!("GIBS request: {e}"))?;
75 if !response.status().is_success() {
76 return Err(format!("GIBS returned {}", response.status()));
77 }
78 let bytes = response
79 .bytes()
80 .await
81 .map_err(|e| format!("GIBS body: {e}"))?;
82 decode_jpeg_rgb(&bytes)
83}