Display labels

Two modes of display:
* ICRSd & GALACTIC frame set the formatting of grid labels to decimal
with digit precision being computed from the grid step selected
* ICRS frame set the formatting to sexagesimal in the format: deg min
sec.ddd .

This fixes #172
This commit is contained in:
bmatthieu3
2025-03-19 20:19:18 +01:00
committed by Matthieu Baumann
parent ee2eb6e704
commit ebb9d6d3d6
25 changed files with 289 additions and 354 deletions

View File

@@ -20,6 +20,7 @@
showSettingsControl: true, showSettingsControl: true,
showStackLayerControl: true, showStackLayerControl: true,
samp: true, samp: true,
showCooGrid: true,
}); });
aladin.addCatalog(A.catalogFromSimbad('M 82', 0.1, {onClick: 'showTable'})); aladin.addCatalog(A.catalogFromSimbad('M 82', 0.1, {onClick: 'showTable'}));

View File

@@ -1,8 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use std::fmt;
#[wasm_bindgen(raw_module = "../../js/libs/astro/coo.js")] #[wasm_bindgen(raw_module = "../../js/libs/astro/coo.js")]
extern "C" { extern "C" {
#[wasm_bindgen(js_name = Format)] #[wasm_bindgen(js_name = Format)]
@@ -28,28 +26,11 @@ extern "C" {
pub fn toDecimal(num: f64, prec: u8) -> String; pub fn toDecimal(num: f64, prec: u8) -> String;
} }
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] use std::cmp::Eq;
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
#[wasm_bindgen] #[wasm_bindgen]
pub enum AngleSerializeFmt { pub enum Formatter {
DMM, Sexagesimal,
DD, Decimal
DMS, }
HMS,
}
impl fmt::Display for AngleSerializeFmt {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Write strictly the first element into the supplied output
// stream: `f`. Returns `fmt::Result` which indicates whether the
// operation succeeded or failed. Note that `write!` uses syntax which
// is very similar to `println!`.
let str = match self {
Self::DMM => "DMM",
Self::DD => "DD",
Self::DMS => "DMS",
Self::HMS => "HMS",
};
write!(f, "{}", str)
}
}

View File

@@ -1,12 +1,9 @@
use wasm_bindgen::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::angle_fmt::AngleSerializeFmt; use crate::angle::Formatter;
use super::color::ColorRGB; use super::color::ColorRGB;
#[wasm_bindgen]
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct GridCfg { pub struct GridCfg {
@@ -22,7 +19,7 @@ pub struct GridCfg {
#[serde(default = "default_enabled")] #[serde(default = "default_enabled")]
pub enabled: Option<bool>, pub enabled: Option<bool>,
#[serde(default = "default_fmt")] #[serde(default = "default_fmt")]
pub fmt: Option<AngleSerializeFmt>, pub fmt: Option<Formatter>,
} }
fn default_labels() -> Option<bool> { fn default_labels() -> Option<bool> {
@@ -45,6 +42,6 @@ fn default_thickness() -> Option<f32> {
None None
} }
fn default_fmt() -> Option<AngleSerializeFmt> { fn default_fmt() -> Option<Formatter> {
None None
} }

View File

@@ -13,7 +13,7 @@ pub mod resources;
pub mod cell; pub mod cell;
pub mod fov; pub mod fov;
pub mod image; pub mod image;
pub mod angle_fmt; pub mod angle;
pub trait Abort { pub trait Abort {
type Item; type Item;

View File

@@ -20,6 +20,7 @@ use crate::{
tile_fetcher::TileFetcherQueue, tile_fetcher::TileFetcherQueue,
time::DeltaTime, time::DeltaTime,
}; };
use crate::math::angle::ToAngle;
use al_core::image::format::ChannelType; use al_core::image::format::ChannelType;
use wcs::WCS; use wcs::WCS;
@@ -421,7 +422,7 @@ impl App {
let v = cell.vertices(); let v = cell.vertices();
let proj2screen = |(lon, lat): &(f64, f64)| -> Option<[f64; 2]> { let proj2screen = |(lon, lat): &(f64, f64)| -> Option<[f64; 2]> {
// 1. convert to xyzw // 1. convert to xyzw
let xyzw = crate::math::lonlat::radec_to_xyzw(Angle(*lon), Angle(*lat)); let xyzw = crate::math::lonlat::radec_to_xyzw(lon.to_angle(), lat.to_angle());
// 2. get it back to the camera frame system // 2. get it back to the camera frame system
let xyzw = crate::coosys::apply_coo_system( let xyzw = crate::coosys::apply_coo_system(
CooSystem::ICRS, CooSystem::ICRS,
@@ -1325,7 +1326,7 @@ impl App {
Ok(()) Ok(())
} }
pub(crate) fn get_max_fov(&self) -> f64 { pub(crate) fn get_max_fov(&self) -> Angle<f64> {
self.projection.aperture_start() self.projection.aperture_start()
} }

View File

@@ -41,7 +41,7 @@ pub fn build_fov_coverage(
let hpx_idxs_iter = vertices_iter.map(|v| { let hpx_idxs_iter = vertices_iter.map(|v| {
let (lon, lat) = crate::math::lonlat::xyzw_to_radec(&v); let (lon, lat) = crate::math::lonlat::xyzw_to_radec(&v);
::healpix::nested::hash(depth, lon.0, lat.0) ::healpix::nested::hash(depth, lon.to_radians(), lat.to_radians())
}); });
HEALPixCoverage::from_fixed_hpx_cells(depth, hpx_idxs_iter, Some(vertices.len())) HEALPixCoverage::from_fixed_hpx_cells(depth, hpx_idxs_iter, Some(vertices.len()))

View File

@@ -88,7 +88,6 @@ const MAX_DPI_LIMIT: f32 = 2.0;
use crate::math; use crate::math;
use crate::time::Time; use crate::time::Time;
use crate::Abort; use crate::Abort;
use crate::ArcDeg;
impl CameraViewPort { impl CameraViewPort {
pub fn new( pub fn new(
gl: &WebGlContext, gl: &WebGlContext,
@@ -97,7 +96,7 @@ impl CameraViewPort {
) -> CameraViewPort { ) -> CameraViewPort {
let last_user_action = UserAction::Starting; let last_user_action = UserAction::Starting;
let aperture = Angle(projection.aperture_start()); let aperture = projection.aperture_start();
let w2m = Matrix4::identity(); let w2m = Matrix4::identity();
let m2w = w2m; let m2w = w2m;
@@ -350,12 +349,12 @@ impl CameraViewPort {
_ => true, _ => true,
}; };
let aperture_start: Angle<f64> = ArcDeg(proj.aperture_start()).into(); let aperture_start = proj.aperture_start();
self.clip_zoom_factor = if aperture > aperture_start { self.clip_zoom_factor = if aperture > aperture_start {
//al_core::log(&format!("a: {:?}, as: {:?}", aperture, aperture_start)); //al_core::log(&format!("a: {:?}, as: {:?}", aperture, aperture_start));
if can_unzoom_more { if can_unzoom_more {
aperture.0 / aperture_start.0 aperture.to_radians() / aperture_start.to_radians()
} else { } else {
1.0 1.0
} }
@@ -363,8 +362,8 @@ impl CameraViewPort {
// Compute the new clip zoom factor // Compute the new clip zoom factor
let a = aperture.abs(); let a = aperture.abs();
let v0 = math::lonlat::radec_to_xyzw(-a / 2.0, Angle(0.0)); let v0 = math::lonlat::radec_to_xyzw(-a / 2.0, 0.0.to_angle());
let v1 = math::lonlat::radec_to_xyzw(a / 2.0, Angle(0.0)); let v1 = math::lonlat::radec_to_xyzw(a / 2.0, 0.0.to_angle());
// Vertex in the WCS of the FOV // Vertex in the WCS of the FOV
if self.width < self.height { if self.width < self.height {

View File

@@ -39,8 +39,8 @@ mod tests {
let gal_lonlat = let gal_lonlat =
super::apply_coo_system(CooSystem::ICRS, CooSystem::GAL, &lonlat.vector()).lonlat(); super::apply_coo_system(CooSystem::ICRS, CooSystem::GAL, &lonlat.vector()).lonlat();
let gal_lon_deg = gal_lonlat.lon().0 * 360.0 / (2.0 * std::f64::consts::PI); let gal_lon_deg = gal_lonlat.lon().to_degrees();
let gal_lat_deg = gal_lonlat.lat().0 * 360.0 / (2.0 * std::f64::consts::PI); let gal_lat_deg = gal_lonlat.lat().to_degrees();
assert_delta!(gal_lon_deg, 96.33723581, 1e-3); assert_delta!(gal_lon_deg, 96.33723581, 1e-3);
assert_delta!(gal_lat_deg, -60.18845577, 1e-3); assert_delta!(gal_lat_deg, -60.18845577, 1e-3);
@@ -56,8 +56,8 @@ mod tests {
let lonlat: LonLatT<f64> = LonLatT::new(ArcDeg(0.0).into(), ArcDeg(0.0).into()); let lonlat: LonLatT<f64> = LonLatT::new(ArcDeg(0.0).into(), ArcDeg(0.0).into());
let j2000_lonlat = let j2000_lonlat =
super::apply_coo_system(CooSystem::GAL, CooSystem::ICRS, &lonlat.vector()).lonlat(); super::apply_coo_system(CooSystem::GAL, CooSystem::ICRS, &lonlat.vector()).lonlat();
let j2000_lon_deg = j2000_lonlat.lon().0 * 360.0 / (2.0 * std::f64::consts::PI); let j2000_lon_deg = j2000_lonlat.lon().to_degrees();
let j2000_lat_deg = j2000_lonlat.lat().0 * 360.0 / (2.0 * std::f64::consts::PI); let j2000_lat_deg = j2000_lonlat.lat().to_degrees();
assert_delta!(j2000_lon_deg, 266.40506655, 1e-3); assert_delta!(j2000_lon_deg, 266.40506655, 1e-3);
assert_delta!(j2000_lat_deg, -28.93616241, 1e-3); assert_delta!(j2000_lat_deg, -28.93616241, 1e-3);
@@ -77,8 +77,8 @@ mod tests {
let gal_lonlat = super::apply_coo_system(CooSystem::ICRS, CooSystem::GAL, &icrs_pos); let gal_lonlat = super::apply_coo_system(CooSystem::ICRS, CooSystem::GAL, &icrs_pos);
let gal_lon_deg = gal_lonlat.lon().0 * 360.0 / (2.0 * std::f64::consts::PI); let gal_lon_deg = gal_lonlat.lon().to_degrees();
let gal_lat_deg = gal_lonlat.lat().0 * 360.0 / (2.0 * std::f64::consts::PI); let gal_lat_deg = gal_lonlat.lat().to_degrees();
assert_delta!(gal_lon_deg, 0.0, 1e-3); assert_delta!(gal_lon_deg, 0.0, 1e-3);
assert_delta!(gal_lat_deg, 0.0, 1e-3); assert_delta!(gal_lat_deg, 0.0, 1e-3);

View File

@@ -27,15 +27,14 @@ impl HEALPixCoverage {
let lonlat = vertices_iter let lonlat = vertices_iter
.map(|vertex| { .map(|vertex| {
let LonLatT(lon, lat) = vertex.lonlat(); let LonLatT(lon, lat) = vertex.lonlat();
//let (lon, lat) = math::lonlat::xyzw_to_radec(&vertex); (lon.to_radians(), lat.to_radians())
(lon.0, lat.0)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let LonLatT(in_lon, in_lat) = inside.lonlat(); let LonLatT(in_lon, in_lat) = inside.lonlat();
let moc = RangeMOC::from_polygon_with_control_point( let moc = RangeMOC::from_polygon_with_control_point(
&lonlat[..], &lonlat[..],
(in_lon.0, in_lat.0), (in_lon.to_radians(), in_lat.to_radians()),
depth, depth,
CellSelection::All, CellSelection::All,
); );
@@ -84,11 +83,11 @@ impl HEALPixCoverage {
pub fn contains_coo(&self, coo: &Vector4<f64>) -> bool { pub fn contains_coo(&self, coo: &Vector4<f64>) -> bool {
let (lon, lat) = math::lonlat::xyzw_to_radec(coo); let (lon, lat) = math::lonlat::xyzw_to_radec(coo);
self.0.is_in(lon.0, lat.0) self.0.is_in(lon.to_radians(), lat.to_radians())
} }
pub fn contains_lonlat(&self, lonlat: &LonLatT<f64>) -> bool { pub fn contains_lonlat(&self, lonlat: &LonLatT<f64>) -> bool {
self.0.is_in(lonlat.lon().0, lonlat.lat().0) self.0.is_in(lonlat.lon().to_radians(), lonlat.lat().to_radians())
} }
// O(log2(N)) // O(log2(N))

View File

@@ -1,5 +1,6 @@
use crate::healpix::cell::HEALPixCell; use crate::healpix::cell::HEALPixCell;
use crate::math::{angle::Angle, lonlat::LonLatT}; use crate::math::lonlat::LonLatT;
use crate::math::angle::ToAngle;
/// A simple wrapper around sore core methods /// A simple wrapper around sore core methods
/// of cdshealpix /// of cdshealpix
/// ///
@@ -17,15 +18,15 @@ pub fn vertices_lonlat<S: BaseFloat>(cell: &HEALPixCell) -> [LonLatT<S>; 4] {
let lon = S::from(*lon).unwrap_abort(); let lon = S::from(*lon).unwrap_abort();
let lat = S::from(*lat).unwrap_abort(); let lat = S::from(*lat).unwrap_abort();
(lon, lat) (lon.to_angle(), lat.to_angle())
}) })
.unzip(); .unzip();
[ [
LonLatT::new(Angle(lon[0]), Angle(lat[0])), LonLatT::new(lon[0], lat[0]),
LonLatT::new(Angle(lon[1]), Angle(lat[1])), LonLatT::new(lon[1], lat[1]),
LonLatT::new(Angle(lon[2]), Angle(lat[2])), LonLatT::new(lon[2], lat[2]),
LonLatT::new(Angle(lon[3]), Angle(lat[3])), LonLatT::new(lon[3], lat[3]),
] ]
} }
use crate::Abort; use crate::Abort;
@@ -40,13 +41,13 @@ pub fn grid_lonlat<S: BaseFloat>(cell: &HEALPixCell, n_segments_by_side: u16) ->
let lon = S::from(*lon).unwrap_abort(); let lon = S::from(*lon).unwrap_abort();
let lat = S::from(*lat).unwrap_abort(); let lat = S::from(*lat).unwrap_abort();
LonLatT::new(Angle(lon), Angle(lat)) LonLatT::new(lon.to_angle(), lat.to_angle())
}) })
.collect() .collect()
} }
pub fn hash_with_dxdy(depth: u8, lonlat: &LonLatT<f64>) -> (u64, f64, f64) { pub fn hash_with_dxdy(depth: u8, lonlat: &LonLatT<f64>) -> (u64, f64, f64) {
healpix::nested::hash_with_dxdy(depth, lonlat.lon().0, lonlat.lat().0) healpix::nested::hash_with_dxdy(depth, lonlat.lon().to_radians(), lonlat.lat().to_radians())
} }
pub const MEAN_HPX_CELL_RES: &[f64; 30] = &[ pub const MEAN_HPX_CELL_RES: &[f64; 30] = &[

View File

@@ -562,7 +562,7 @@ impl WebClient {
/// the sinus would be 180 degrees. /// the sinus would be 180 degrees.
#[wasm_bindgen(js_name = getMaxFieldOfView)] #[wasm_bindgen(js_name = getMaxFieldOfView)]
pub fn get_max_fov(&mut self) -> f64 { pub fn get_max_fov(&mut self) -> f64 {
self.app.get_max_fov() self.app.get_max_fov().to_degrees()
} }
/// Get the clip zoom factor of the view /// Get the clip zoom factor of the view

View File

@@ -1,4 +1,5 @@
use cgmath::BaseFloat; use cgmath::BaseFloat;
use crate::Abort;
// ArcDeg wrapper structure // ArcDeg wrapper structure
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct ArcDeg<T: BaseFloat>(pub T); pub struct ArcDeg<T: BaseFloat>(pub T);
@@ -6,23 +7,6 @@ pub struct ArcDeg<T: BaseFloat>(pub T);
//pub const TWICE_PI: f64 = 6.28318530718; //pub const TWICE_PI: f64 = 6.28318530718;
pub const PI: f64 = std::f64::consts::PI; pub const PI: f64 = std::f64::consts::PI;
impl<T> ArcDeg<T>
where
T: BaseFloat,
{
fn get_frac_minutes(&self) -> ArcMin<T> {
let deg = *self;
let frac = deg.fract();
let minutes_per_degree = T::from(60_f32).unwrap_abort();
ArcMin(frac * minutes_per_degree)
}
fn truncate(&mut self) {
*self = Self((*self).trunc());
}
}
use cgmath::{Deg, Rad}; use cgmath::{Deg, Rad};
use serde::Deserialize; use serde::Deserialize;
// Convert a Rad<T> to an ArcDeg<T> // Convert a Rad<T> to an ArcDeg<T>
@@ -71,18 +55,6 @@ where
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct ArcHour<T: BaseFloat>(pub T); pub struct ArcHour<T: BaseFloat>(pub T);
impl<T> ArcHour<T>
where
T: BaseFloat,
{
fn get_frac_minutes(&self) -> ArcMin<T> {
let hour = *self;
let frac = hour.fract();
let minutes_per_hour = T::from(60_f64).unwrap_abort();
ArcMin(minutes_per_hour * frac)
}
}
impl<T> From<Rad<T>> for ArcHour<T> impl<T> From<Rad<T>> for ArcHour<T>
where where
@@ -122,19 +94,6 @@ where
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct ArcMin<T: BaseFloat>(pub T); pub struct ArcMin<T: BaseFloat>(pub T);
impl<T> ArcMin<T>
where
T: BaseFloat,
{
fn get_frac_seconds(&self) -> ArcSec<T> {
let min: ArcMin<T> = *self;
let frac = min.fract();
let seconds_per_min = T::from(60_f64).unwrap_abort();
ArcSec(seconds_per_min * frac)
}
}
// Convert a Rad<T> to an ArcMin<T> // Convert a Rad<T> to an ArcMin<T>
impl<T> From<Rad<T>> for ArcMin<T> impl<T> From<Rad<T>> for ArcMin<T>
where where
@@ -241,6 +200,8 @@ where
} }
} }
use al_api::angle::Format;
/*
pub enum SerializeFmt { pub enum SerializeFmt {
DMS, DMS,
HMS, HMS,
@@ -269,7 +230,7 @@ impl SerializeFmt {
Self::DD => DD::to_string(angle), Self::DD => DD::to_string(angle),
} }
} }
} }*/
/*pub trait SerializeToString { /*pub trait SerializeToString {
fn to_string(&self) -> String; fn to_string(&self) -> String;
@@ -284,6 +245,7 @@ where
} }
}*/ }*/
/*
pub struct DMS; pub struct DMS;
pub struct HMS; pub struct HMS;
pub struct DMM; pub struct DMM;
@@ -315,7 +277,7 @@ impl FormatType for DMM {
result result
} }
} }
use crate::Abort;
impl FormatType for DMS { impl FormatType for DMS {
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String { fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
let angle = Rad(angle.0); let angle = Rad(angle.0);
@@ -361,94 +323,106 @@ impl FormatType for HMS {
result result
} }
} }*/
#[derive(Clone, Copy, Debug, Eq, Hash, Deserialize)] #[derive(Clone, Copy, Debug, Eq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
#[repr(C)] #[repr(C)]
pub struct Angle<S: BaseFloat>(pub S); pub struct Angle<S: BaseFloat> {
pub rad: S,
fmt: AngleFormatter,
}
impl<S> Angle<S> impl<S> Angle<S>
where where
S: BaseFloat, S: BaseFloat,
{ {
pub fn new<T: Into<Rad<S>>>(angle: T) -> Angle<S> { pub fn new<T: Into<Rad<S>>>(angle: T) -> Angle<S> {
let radians: Rad<S> = angle.into(); let radians: Rad<S> = angle.into();
Angle(radians.0) Angle { rad: radians.0, fmt: AngleFormatter::default() }
} }
pub fn cos(&self) -> S { pub fn cos(&self) -> S {
self.0.cos() self.rad.cos()
} }
pub fn sin(&self) -> S { pub fn sin(&self) -> S {
self.0.sin() self.rad.sin()
} }
pub fn tan(&self) -> S { pub fn tan(&self) -> S {
self.0.tan() self.rad.tan()
} }
pub fn asin(self) -> S { pub fn asin(self) -> S {
self.0.asin() self.rad.asin()
} }
pub fn acos(self) -> S { pub fn acos(self) -> S {
self.0.acos() self.rad.acos()
} }
pub fn atan(self) -> S { pub fn atan(self) -> S {
self.0.atan() self.rad.atan()
} }
pub fn atan2(self, other: Self) -> S { pub fn atan2(self, other: Self) -> S {
self.0.atan2(other.0) self.rad.atan2(other.rad)
} }
pub fn floor(self) -> Self { pub fn floor(self) -> Self {
Angle(self.0.floor()) self.rad.floor().to_angle()
} }
pub fn ceil(self) -> Self { pub fn ceil(self) -> Self {
Angle(self.0.ceil()) self.rad.ceil().to_angle()
} }
pub fn round(self) -> Self { pub fn round(self) -> Self {
Angle(self.0.round()) self.rad.round().to_angle()
} }
pub fn trunc(self) -> Self { pub fn trunc(self) -> Self {
Angle(self.0.trunc()) self.rad.trunc().to_angle()
} }
pub fn fract(self) -> S { pub fn fract(self) -> S {
self.0.fract() self.rad.fract()
} }
pub fn abs(self) -> Self { pub fn abs(self) -> Self {
Angle(self.0.abs()) self.rad.abs().to_angle()
} }
pub fn max(self, other: Self) -> Self { pub fn max(self, other: Self) -> Self {
Angle(self.0.max(other.0)) self.rad.max(other.rad).to_angle()
} }
pub fn min(self, other: Self) -> Self { pub fn min(self, other: Self) -> Self {
Angle(self.0.min(other.0)) self.rad.min(other.rad).to_angle()
} }
pub fn min_value() -> Self { pub fn min_value() -> Self {
Angle(S::min_value()) S::min_value().to_angle()
} }
pub fn max_value() -> Self { pub fn max_value() -> Self {
Angle(S::max_value()) S::max_value().to_angle()
} }
pub fn to_radians(&self) -> S { pub fn to_radians(&self) -> S {
self.0 self.rad
} }
pub fn to_degrees(&self) -> S { pub fn to_degrees(&self) -> S {
self.0.to_degrees() self.rad.to_degrees()
}
pub fn to_hours(&self) -> S {
self.to_degrees() / S::from(15.0).unwrap()
}
pub fn set_format(&mut self, fmt: AngleFormatter) {
self.fmt = fmt;
} }
} }
@@ -464,7 +438,7 @@ where
S: BaseFloat, S: BaseFloat,
{ {
fn to_angle(self) -> Angle<S> { fn to_angle(self) -> Angle<S> {
Angle(self) Angle { rad: self, fmt: Default::default() }
} }
} }
@@ -474,7 +448,7 @@ where
S: BaseFloat, S: BaseFloat,
{ {
fn from(rad: Rad<S>) -> Self { fn from(rad: Rad<S>) -> Self {
Angle(rad.0) rad.0.to_angle()
} }
} }
impl<S> From<Angle<S>> for Rad<S> impl<S> From<Angle<S>> for Rad<S>
@@ -482,7 +456,7 @@ where
S: BaseFloat, S: BaseFloat,
{ {
fn from(angle: Angle<S>) -> Self { fn from(angle: Angle<S>) -> Self {
Rad(angle.0) Rad(angle.rad)
} }
} }
@@ -501,7 +475,7 @@ where
{ {
fn eq(&self, other: &T) -> bool { fn eq(&self, other: &T) -> bool {
let angle: Angle<S> = (*other).into(); let angle: Angle<S> = (*other).into();
angle.0 == self.0 angle.rad == self.rad
} }
} }
@@ -513,7 +487,7 @@ where
{ {
fn partial_cmp(&self, other: &T) -> Option<Ordering> { fn partial_cmp(&self, other: &T) -> Option<Ordering> {
let angle: Angle<S> = (*other).into(); let angle: Angle<S> = (*other).into();
self.0.partial_cmp(&angle.0) self.rad.partial_cmp(&angle.rad)
} }
} }
@@ -524,7 +498,7 @@ where
{ {
fn from(deg: ArcDeg<S>) -> Self { fn from(deg: ArcDeg<S>) -> Self {
let rad: Rad<S> = deg.into(); let rad: Rad<S> = deg.into();
Angle(rad.0) rad.0.to_angle()
} }
} }
impl<S> From<Angle<S>> for ArcDeg<S> impl<S> From<Angle<S>> for ArcDeg<S>
@@ -545,7 +519,7 @@ where
{ {
fn from(min: ArcMin<S>) -> Self { fn from(min: ArcMin<S>) -> Self {
let rad: Rad<S> = min.into(); let rad: Rad<S> = min.into();
Angle(rad.0) rad.0.to_angle()
} }
} }
// Convert from ArcSec<S> // Convert from ArcSec<S>
@@ -555,28 +529,11 @@ where
{ {
fn from(sec: ArcSec<S>) -> Self { fn from(sec: ArcSec<S>) -> Self {
let rad: Rad<S> = sec.into(); let rad: Rad<S> = sec.into();
Angle(rad.0) rad.0.to_angle()
} }
} }
/*
impl<S> PartialEq<S> for Angle<S>
where
S: BaseFloat + !AngleUnit<S>,
{
fn eq(&self, other: &S) -> bool {
self.0 == *other
}
}
*/
use std::cmp::Ordering; use std::cmp::Ordering;
/*impl<S> PartialOrd<S> for Angle<S>
where
S: BaseFloat,
{
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
self.0.partial_cmp(other)
}
}*/
use std::ops::Div; use std::ops::Div;
impl<S> Div for Angle<S> impl<S> Div for Angle<S>
@@ -586,8 +543,8 @@ where
type Output = Self; type Output = Self;
fn div(self, rhs: Self) -> Self::Output { fn div(self, rhs: Self) -> Self::Output {
let angle = self.0 / rhs.0; let rad = self.rad / rhs.rad;
Angle(angle) rad.to_angle()
} }
} }
impl<S> Div<S> for Angle<S> impl<S> Div<S> for Angle<S>
@@ -597,8 +554,8 @@ where
type Output = Self; type Output = Self;
fn div(self, rhs: S) -> Self::Output { fn div(self, rhs: S) -> Self::Output {
let angle = self.0 / rhs; let rad = self.rad / rhs;
Angle(angle) rad.to_angle()
} }
} }
@@ -610,8 +567,8 @@ where
type Output = Self; type Output = Self;
fn mul(self, rhs: Self) -> Self::Output { fn mul(self, rhs: Self) -> Self::Output {
let angle = self.0 * rhs.0; let angle = self.rad * rhs.rad;
Angle(angle) angle.to_angle()
} }
} }
impl<S> Mul<S> for Angle<S> impl<S> Mul<S> for Angle<S>
@@ -621,8 +578,8 @@ where
type Output = Self; type Output = Self;
fn mul(self, rhs: S) -> Self::Output { fn mul(self, rhs: S) -> Self::Output {
let angle = self.0 * rhs; let angle = self.rad * rhs;
Angle(angle) angle.to_angle()
} }
} }
@@ -634,8 +591,8 @@ where
type Output = Self; type Output = Self;
fn sub(self, other: Self) -> Self::Output { fn sub(self, other: Self) -> Self::Output {
let angle = self.0 - other.0; let angle = self.rad - other.rad;
Angle(angle) angle.to_angle()
} }
} }
impl<S> Sub<S> for Angle<S> impl<S> Sub<S> for Angle<S>
@@ -645,8 +602,8 @@ where
type Output = Self; type Output = Self;
fn sub(self, other: S) -> Self::Output { fn sub(self, other: S) -> Self::Output {
let angle = self.0 - other; let angle = self.rad - other;
Angle(angle) angle.to_angle()
} }
} }
@@ -658,8 +615,8 @@ where
type Output = Self; type Output = Self;
fn add(self, other: Self) -> Self::Output { fn add(self, other: Self) -> Self::Output {
let angle = self.0 + other.0; let angle = self.rad + other.rad;
Angle(angle) angle.to_angle()
} }
} }
impl<S> Add<S> for Angle<S> impl<S> Add<S> for Angle<S>
@@ -669,8 +626,8 @@ where
type Output = Self; type Output = Self;
fn add(self, other: S) -> Self::Output { fn add(self, other: S) -> Self::Output {
let angle = self.0 + other; let angle = self.rad + other;
Angle(angle) angle.to_angle()
} }
} }
@@ -718,8 +675,56 @@ where
type Output = Self; type Output = Self;
fn rem(self, other: Self) -> Self::Output { fn rem(self, other: Self) -> Self::Output {
let angle = self.0 % other.0; let angle = self.rad % other.rad;
Angle(angle) angle.to_angle()
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash, Deserialize)]
pub enum AngleFormatter {
Sexagesimal {
/// Number of digit of precision for the unit value
/// (interpreted as hours or degrees depending of the hours boolean field)
prec: u8,
/// Whether a '+' is added
plus: bool,
/// HMS or DMS
hours: bool,
},
Decimal {
/// Number of digit of precision
prec: u8,
}
}
impl Default for AngleFormatter {
fn default() -> Self {
AngleFormatter::Decimal { prec: 8 }
}
}
use std::fmt::Display;
impl Display for Angle<f64> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.fmt {
AngleFormatter::Sexagesimal { prec, plus, hours } => {
let unit = if hours {
self.to_hours()
} else {
self.to_degrees()
};
// Round at a specific number of digit of precision
let pw = 10.0_f64.powi(prec as i32);
let unit = (unit * pw).round() / pw;
// Format the unit value to sexagesimal.
// The precision 8 corresponds to the formatting: deg/hour min sec.ddd
write!(f, "{}", Format::toSexagesimal(unit, 8, plus))
},
AngleFormatter::Decimal { prec } => {
write!(f, "{:.1$}°", self.to_degrees(), prec as usize)
}
}
} }
} }
@@ -730,18 +735,18 @@ where
{ {
type Output = Self; type Output = Self;
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
Angle(-self.0) (-self.rad).to_angle()
} }
} }
use al_core::{shader::UniformType, WebGlContext}; use al_core::{shader::UniformType, WebGlContext};
use web_sys::WebGlUniformLocation; use web_sys::WebGlUniformLocation;
impl UniformType for Angle<f32> { impl UniformType for Angle<f32> {
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) { fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
gl.uniform1f(location, value.0); gl.uniform1f(location, value.rad);
} }
} }
impl UniformType for Angle<f64> { impl UniformType for Angle<f64> {
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) { fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
gl.uniform1f(location, value.0 as f32); gl.uniform1f(location, value.rad as f32);
} }
} }

View File

@@ -26,8 +26,8 @@ where
/// * ``lon`` - Longitude /// * ``lon`` - Longitude
/// * ``lat`` - Latitude /// * ``lat`` - Latitude
pub fn new(mut lon: Angle<S>, lat: Angle<S>) -> LonLatT<S> { pub fn new(mut lon: Angle<S>, lat: Angle<S>) -> LonLatT<S> {
if lon.0 < S::zero() { if lon.to_radians() < S::zero() {
lon.0 = lon.0 + S::from(TWICE_PI).unwrap_abort(); lon = lon + S::from(TWICE_PI).unwrap_abort();
} }
LonLatT(lon, lat) LonLatT(lon, lat)
@@ -156,33 +156,31 @@ where
#[inline] #[inline]
pub fn ang_between_lonlat<S: BaseFloat>(lonlat1: LonLatT<S>, lonlat2: LonLatT<S>) -> Angle<S> { pub fn ang_between_lonlat<S: BaseFloat>(lonlat1: LonLatT<S>, lonlat2: LonLatT<S>) -> Angle<S> {
let abs_diff_lon = (lonlat1.lon() - lonlat2.lon()).abs(); let abs_diff_lon = (lonlat1.lon() - lonlat2.lon()).abs();
Angle( (lonlat1.lat().sin() * lonlat2.lat().sin()
(lonlat1.lat().sin() * lonlat2.lat().sin() + lonlat1.lat().cos() * lonlat2.lat().cos() * abs_diff_lon.cos())
+ lonlat1.lat().cos() * lonlat2.lat().cos() * abs_diff_lon.cos()) .acos().to_angle()
.acos(),
)
} }
#[inline] #[inline]
pub fn xyz_to_radec<S: BaseFloat>(v: &Vector3<S>) -> (Angle<S>, Angle<S>) { pub fn xyz_to_radec<S: BaseFloat>(v: &Vector3<S>) -> (Angle<S>, Angle<S>) {
let lon = Angle(v.x.atan2(v.z)); let lon = (v.x.atan2(v.z)).to_angle();
let lat = Angle(v.y.atan2((v.x * v.x + v.z * v.z).sqrt())); let lat = (v.y.atan2((v.x * v.x + v.z * v.z).sqrt())).to_angle();
(lon, lat) (lon, lat)
} }
#[inline] #[inline]
pub fn xyzw_to_radec<S: BaseFloat>(v: &Vector4<S>) -> (Angle<S>, Angle<S>) { pub fn xyzw_to_radec<S: BaseFloat>(v: &Vector4<S>) -> (Angle<S>, Angle<S>) {
let lon = Angle(v.x.atan2(v.z)); let lon = (v.x.atan2(v.z)).to_angle();
let lat = Angle(v.y.atan2((v.x * v.x + v.z * v.z).sqrt())); let lat = (v.y.atan2((v.x * v.x + v.z * v.z).sqrt())).to_angle();
(lon, lat) (lon, lat)
} }
#[inline] #[inline]
pub fn radec_to_xyz<S: BaseFloat>(theta: Angle<S>, delta: Angle<S>) -> Vector3<S> { pub fn radec_to_xyz<S: BaseFloat>(theta: Angle<S>, delta: Angle<S>) -> Vector3<S> {
let (ds, dc) = delta.0.sin_cos(); let (ds, dc) = delta.to_radians().sin_cos();
let (ts, tc) = theta.0.sin_cos(); let (ts, tc) = theta.to_radians().sin_cos();
Vector3::<S>::new(dc * ts, ds, dc * tc) Vector3::<S>::new(dc * ts, ds, dc * tc)
} }

View File

@@ -19,8 +19,10 @@ use cgmath::Vector2;
pub mod coo_space; pub mod coo_space;
pub mod domain; pub mod domain;
use domain::{basic, full::FullScreen}; use crate::math::angle::ToAngle;
use domain::{basic, full::FullScreen};
use crate::math::angle::Angle;
/* S <-> NDC space conversion methods */ /* S <-> NDC space conversion methods */
pub fn screen_to_ndc_space( pub fn screen_to_ndc_space(
pos_screen_space: &XYScreen<f64>, pos_screen_space: &XYScreen<f64>,
@@ -304,7 +306,7 @@ impl ProjectionType {
} }
}*/ }*/
pub fn bounds_size_ratio(&self) -> f64 { pub const fn bounds_size_ratio(&self) -> f64 {
match self { match self {
// Zenithal projections // Zenithal projections
/* TAN, Gnomonic projection */ /* TAN, Gnomonic projection */
@@ -355,17 +357,17 @@ impl ProjectionType {
} }
} }
pub fn aperture_start(&self) -> f64 { pub fn aperture_start(&self) -> Angle<f64> {
match self { match self {
// Zenithal projections // Zenithal projections
/* TAN, Gnomonic projection */ /* TAN, Gnomonic projection */
ProjectionType::Tan(_) => 150.0, ProjectionType::Tan(_) => 150.0_f64.to_radians().to_angle(),
/* STG, Stereographic projection */ /* STG, Stereographic projection */
ProjectionType::Stg(_) => 360.0, ProjectionType::Stg(_) => 360.0_f64.to_radians().to_angle(),
/* SIN, Orthographic */ /* SIN, Orthographic */
ProjectionType::Sin(_) => 180.0, ProjectionType::Sin(_) => 180.0_f64.to_radians().to_angle(),
/* ZEA, Equal-area */ /* ZEA, Equal-area */
ProjectionType::Zea(_) => 360.0, ProjectionType::Zea(_) => 360.0_f64.to_radians().to_angle(),
/* FEYE, Fish-eyes */ /* FEYE, Fish-eyes */
//ProjectionType::Feye(_) => 190.0, //ProjectionType::Feye(_) => 190.0,
/* AIR, */ /* AIR, */
@@ -379,9 +381,9 @@ impl ProjectionType {
// Pseudo-cylindrical projections // Pseudo-cylindrical projections
/* AIT, Aitoff */ /* AIT, Aitoff */
ProjectionType::Ait(_) => 360.0, ProjectionType::Ait(_) => 360.0_f64.to_radians().to_angle(),
// MOL, Mollweide */ // MOL, Mollweide */
ProjectionType::Mol(_) => 360.0, ProjectionType::Mol(_) => 360.0_f64.to_radians().to_angle(),
// PAR, */ // PAR, */
//ProjectionType::Par(_) => 360.0, //ProjectionType::Par(_) => 360.0,
// SFL, */ // SFL, */
@@ -389,7 +391,7 @@ impl ProjectionType {
// Cylindrical projections // Cylindrical projections
// MER, Mercator */ // MER, Mercator */
ProjectionType::Mer(_) => 360.0, ProjectionType::Mer(_) => 360.0_f64.to_radians().to_angle(),
// CAR, */ // CAR, */
//ProjectionType::Car(_) => 360.0, //ProjectionType::Car(_) => 360.0,
// CEA, */ // CEA, */

View File

@@ -2,6 +2,7 @@ use crate::math;
use cgmath::{BaseFloat, InnerSpace}; use cgmath::{BaseFloat, InnerSpace};
use cgmath::{Euler, Quaternion}; use cgmath::{Euler, Quaternion};
use cgmath::{Vector3, Vector4}; use cgmath::{Vector3, Vector4};
use crate::math::angle::ToAngle;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
// Internal structure of a rotation, a quaternion // Internal structure of a rotation, a quaternion
@@ -159,7 +160,7 @@ where
let a = m.x.z.atan2(m.z.z); let a = m.x.z.atan2(m.z.z);
let b = (-m.z.y).atan2((S::one() - m.z.y * m.z.y).sqrt()); let b = (-m.z.y).atan2((S::one() - m.z.y * m.z.y).sqrt());
let c = m.x.y.atan2(m.y.y); let c = m.x.y.atan2(m.y.y);
(Angle(a), Angle(b), Angle(c)) (a.to_angle(), b.to_angle(), c.to_angle())
} }
} }

View File

@@ -1,15 +1,16 @@
use crate::math::angle::Angle; use crate::math::angle::Angle;
use cgmath::{BaseFloat, InnerSpace, Vector2, Vector3}; use cgmath::{BaseFloat, InnerSpace, Vector2, Vector3};
use crate::math::angle::ToAngle;
#[inline] #[inline]
pub fn angle2<S: BaseFloat>(ab: &Vector2<S>, bc: &Vector2<S>) -> Angle<S> { pub fn angle2<S: BaseFloat>(ab: &Vector2<S>, bc: &Vector2<S>) -> Angle<S> {
Angle((ab.dot(*bc)).acos()) ((ab.dot(*bc)).acos()).to_angle()
} }
#[inline] #[inline]
pub fn angle3<S: BaseFloat>(x: &Vector3<S>, y: &cgmath::Vector3<S>) -> Angle<S> { pub fn angle3<S: BaseFloat>(x: &Vector3<S>, y: &cgmath::Vector3<S>) -> Angle<S> {
let theta = x.cross(*y).magnitude().atan2(x.dot(*y)); let theta = x.cross(*y).magnitude().atan2(x.dot(*y));
Angle(theta) theta.to_angle()
} }
#[inline] #[inline]

View File

@@ -6,12 +6,13 @@ use crate::ProjectionType;
use cgmath::InnerSpace; use cgmath::InnerSpace;
use cgmath::Vector3; use cgmath::Vector3;
use crate::math::angle::SerializeFmt;
use crate::math::lonlat::LonLat; use crate::math::lonlat::LonLat;
use crate::math::projection::coo_space::XYScreen; use crate::math::projection::coo_space::XYScreen;
use crate::math::TWICE_PI; use crate::math::TWICE_PI;
use crate::math::angle::ToAngle; use crate::math::angle::ToAngle;
use crate::math::angle::AngleFormatter;
use al_api::angle::Formatter;
use cgmath::Vector2; use cgmath::Vector2;
use core::ops::Range; use core::ops::Range;
@@ -22,7 +23,6 @@ pub enum LabelOptions {
Centered, Centered,
OnSide, OnSide,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Label { pub struct Label {
// The position // The position
@@ -39,7 +39,8 @@ impl Label {
options: LabelOptions, options: LabelOptions,
camera: &CameraViewPort, camera: &CameraViewPort,
projection: &ProjectionType, projection: &ProjectionType,
_fmt: &SerializeFmt, fmt: Formatter,
grid_decimal_prec: u8
) -> Option<Self> { ) -> Option<Self> {
let fov = camera.get_field_of_view(); let fov = camera.get_field_of_view();
let d = if fov.contains_north_pole() { let d = if fov.contains_north_pole() {
@@ -76,8 +77,18 @@ impl Label {
lon += TWICE_PI; lon += TWICE_PI;
} }
//let content = fmt.to_string(lon.to_angle()); let mut angle = lon.to_angle();
let content = al_api::angle_fmt::Format::toSexagesimal(lon.to_degrees() / 15.0, 8, false); let fmt = match fmt {
Formatter::Decimal => {
AngleFormatter::Decimal { prec: grid_decimal_prec }
},
Formatter::Sexagesimal => {
// Sexagesimal formatting for longitudes is HMS
AngleFormatter::Sexagesimal { prec: grid_decimal_prec, plus: false, hours: true }
}
};
angle.set_format(fmt);
let content = angle.to_string();
let position = if !fov.is_allsky() { let position = if !fov.is_allsky() {
d1 + OFF_TANGENT * dt - OFF_BI_TANGENT * db d1 + OFF_TANGENT * dt - OFF_BI_TANGENT * db
@@ -101,6 +112,8 @@ impl Label {
options: LabelOptions, options: LabelOptions,
camera: &CameraViewPort, camera: &CameraViewPort,
projection: &ProjectionType, projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
) -> Option<Self> { ) -> Option<Self> {
let lonlat = match options { let lonlat = match options {
LabelOptions::Centered => { LabelOptions::Centered => {
@@ -130,8 +143,18 @@ impl Label {
let dt = (d2 - d1).normalize(); let dt = (d2 - d1).normalize();
let db = Vector2::new(dt.y.abs(), dt.x.abs()); let db = Vector2::new(dt.y.abs(), dt.x.abs());
//let content = SerializeFmt::DMS.to_string(lonlat.lat()); let mut angle = lat.to_angle();
let content = al_api::angle_fmt::Format::toSexagesimal(lonlat.lat().to_degrees(), 7, false); let fmt = match fmt {
Formatter::Decimal => {
AngleFormatter::Decimal { prec: grid_decimal_prec }
},
Formatter::Sexagesimal => {
// Sexagesimal formatting for latitudes is DMS with an optional '+' character
AngleFormatter::Sexagesimal { prec: grid_decimal_prec, plus: true, hours: false }
}
};
angle.set_format(fmt);
let content = angle.to_string();
let fov = camera.get_field_of_view(); let fov = camera.get_field_of_view();
let position = if !fov.is_allsky() && !fov.contains_pole() { let position = if !fov.is_allsky() && !fov.contains_pole() {

View File

@@ -1,3 +1,5 @@
use al_api::angle::Formatter;
use super::label::{Label, LabelOptions}; use super::label::{Label, LabelOptions};
use crate::math::lonlat::LonLat; use crate::math::lonlat::LonLat;
use crate::math::sph_geom::region::Intersection; use crate::math::sph_geom::region::Intersection;
@@ -7,14 +9,14 @@ use core::ops::Range;
use crate::math::MINUS_HALF_PI; use crate::math::MINUS_HALF_PI;
use crate::ProjectionType; use crate::ProjectionType;
use super::angle::SerializeFmt;
use crate::math::HALF_PI; use crate::math::HALF_PI;
pub fn get_intersecting_meridian( pub fn get_intersecting_meridian(
lon: f64, lon: f64,
camera: &CameraViewPort, camera: &CameraViewPort,
projection: &ProjectionType, projection: &ProjectionType,
fmt: &SerializeFmt, fmt: Formatter,
grid_decimal_prec: u8
) -> Option<Meridian> { ) -> Option<Meridian> {
let fov = camera.get_field_of_view(); let fov = camera.get_field_of_view();
if fov.contains_both_poles() { if fov.contains_both_poles() {
@@ -25,6 +27,7 @@ pub fn get_intersecting_meridian(
camera, camera,
projection, projection,
fmt, fmt,
grid_decimal_prec
); );
Some(meridian) Some(meridian)
} else { } else {
@@ -39,6 +42,7 @@ pub fn get_intersecting_meridian(
camera, camera,
projection, projection,
fmt, fmt,
grid_decimal_prec
); );
Some(meridian) Some(meridian)
} }
@@ -56,7 +60,7 @@ pub fn get_intersecting_meridian(
lat1..MINUS_HALF_PI lat1..MINUS_HALF_PI
}; };
Meridian::new(lon, &lat, LabelOptions::OnSide, camera, projection, fmt) Meridian::new(lon, &lat, LabelOptions::OnSide, camera, projection, fmt, grid_decimal_prec)
} }
2 => { 2 => {
// full intersection // full intersection
@@ -73,56 +77,18 @@ pub fn get_intersecting_meridian(
camera, camera,
projection, projection,
fmt, fmt,
grid_decimal_prec
) )
} }
_ => { _ => Meridian::new(
/*let mut vertices = vertices.into_vec(); lon,
// One segment over two will be in the field of view &(-HALF_PI..HALF_PI),
vertices.push(Vector4::new(0.0, 1.0, 0.0, 1.0)); LabelOptions::OnSide,
vertices.push(Vector4::new(0.0, -1.0, 0.0, 1.0)); camera,
projection,
vertices.sort_by(|i1, i2| { fmt,
i1.y.total_cmp(&i2.y) grid_decimal_prec
}); )
let v1 = &vertices[0];
let v2 = &vertices[1];
// meridian are part of great circles so the mean between v1 & v2 also lies on it
let vm = (v1 + v2).truncate().normalize();
let vertices = if !fov.contains_south_pole() {
&vertices[1..]
} else {
&vertices
};
let line_vertices = vertices.iter().zip(vertices.iter().skip(1))
.step_by(2)
.map(|(i1, i2)| {
line::great_circle_arc::project(
lon,
i1.lat().to_radians(),
lon,
i2.lat().to_radians(),
camera,
projection
)
})
.flatten()
.collect::<Vec<_>>();
let label = Label::from_meridian(&v1.lonlat(), camera, projection, fmt);
*/
Meridian::new(
lon,
&(-HALF_PI..HALF_PI),
LabelOptions::OnSide,
camera,
projection,
fmt,
)
}
}; };
Some(meridian) Some(meridian)
@@ -139,7 +105,6 @@ pub struct Meridian {
indices: Vec<Range<usize>>, indices: Vec<Range<usize>>,
label: Option<Label>, label: Option<Label>,
} }
impl Meridian { impl Meridian {
pub fn new( pub fn new(
lon: f64, lon: f64,
@@ -147,9 +112,10 @@ impl Meridian {
label_options: LabelOptions, label_options: LabelOptions,
camera: &CameraViewPort, camera: &CameraViewPort,
projection: &ProjectionType, projection: &ProjectionType,
fmt: &SerializeFmt, fmt: Formatter,
grid_decimal_prec: u8
) -> Self { ) -> Self {
let label = Label::from_meridian(lon, lat, label_options, camera, projection, fmt); let label = Label::from_meridian(lon, lat, label_options, camera, projection, fmt, grid_decimal_prec);
// Draw the full parallel // Draw the full parallel
let vertices = crate::renderable::line::great_circle_arc::project( let vertices = crate::renderable::line::great_circle_arc::project(
@@ -185,52 +151,6 @@ impl Meridian {
indices.push(start_idx..vertices.len()); indices.push(start_idx..vertices.len());
/*let mut prev_v = [vertices[0].x as f32, vertices[0].y as f32];
let vertices: Vec<_> = std::iter::once(prev_v)
.chain(
vertices.into_iter().skip(1)
.filter_map(|v| {
let cur_v = [v.x as f32, v.y as f32];
if cur_v == prev_v {
None
} else {
prev_v = cur_v;
Some(cur_v)
}
})
)
.collect();
// Create subsets of vertices referring to different lines
let indices = if vertices.len() >= 3 {
let mut indices = vec![];
let mut v0 = 0;
let mut v1 = 1;
let mut v2 = 2;
let mut s = 0;
let n_segment = vertices.len() - 1;
for i in 0..n_segment {
if Triangle::new(&vertices[v0], &vertices[v1], &vertices[v2]).is_valid(camera) {
indices.push(s..(i+1));
s = i;
}
v0 = v1;
v1 = v2;
v2 = (v2 + 1) % vertices.len();
}
//indices.push(start_line_i..vertices.len());
//vec![0..vertices.len()]
vec![0..2]
} else {
vec![0..vertices.len()]
};*/
Self { Self {
vertices, vertices,
indices, indices,

View File

@@ -8,7 +8,6 @@ use al_core::VecData;
use parallel::Parallel; use parallel::Parallel;
use crate::camera::CameraViewPort; use crate::camera::CameraViewPort;
use crate::math::angle;
use crate::math::HALF_PI; use crate::math::HALF_PI;
use crate::ProjectionType; use crate::ProjectionType;
use al_api::color::ColorRGBA; use al_api::color::ColorRGBA;
@@ -28,7 +27,7 @@ pub struct ProjetedGrid {
// Render Text Manager // Render Text Manager
text_renderer: TextRenderManager, text_renderer: TextRenderManager,
fmt: angle::SerializeFmt, fmt: Formatter,
//line_style: line::Style, //line_style: line::Style,
meridians: Vec<Meridian>, meridians: Vec<Meridian>,
@@ -41,9 +40,8 @@ use crate::renderable::text::TextRenderManager;
use crate::renderable::Renderer; use crate::renderable::Renderer;
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
use web_sys::HtmlElement; use web_sys::HtmlElement;
use al_api::angle::Formatter;
use self::meridian::Meridian; use self::meridian::Meridian;
impl ProjetedGrid { impl ProjetedGrid {
pub fn new(gl: WebGlContext, aladin_div: &HtmlElement) -> Result<ProjetedGrid, JsValue> { pub fn new(gl: WebGlContext, aladin_div: &HtmlElement) -> Result<ProjetedGrid, JsValue> {
let text_renderer = TextRenderManager::new(aladin_div)?; let text_renderer = TextRenderManager::new(aladin_div)?;
@@ -58,7 +56,7 @@ impl ProjetedGrid {
let enabled = false; let enabled = false;
let label_scale = 1.0; let label_scale = 1.0;
//let line_style = line::Style::None; //let line_style = line::Style::None;
let fmt = angle::SerializeFmt::DMS; let fmt = Formatter::Decimal;
let thickness = 2.0; let thickness = 2.0;
let meridians = Vec::new(); let meridians = Vec::new();
let parallels = Vec::new(); let parallels = Vec::new();
@@ -150,7 +148,7 @@ impl ProjetedGrid {
} }
if let Some(fmt) = fmt { if let Some(fmt) = fmt {
self.fmt = fmt.into(); self.fmt = fmt;
} }
if let Some(label_size) = label_size { if let Some(label_size) = label_size {
@@ -210,6 +208,8 @@ impl ProjetedGrid {
(bbox.get_lon_size() as f64) * step_line_px / (camera.get_width() as f64); (bbox.get_lon_size() as f64) * step_line_px / (camera.get_width() as f64);
let step_lon = select_fixed_step(step_lon_precised); let step_lon = select_fixed_step(step_lon_precised);
let decimal_lon_prec = step_lon.to_degrees().log10().abs().ceil() as u8;
// Add meridians // Add meridians
let start_lon = bbox.lon_min() - (bbox.lon_min() % step_lon); let start_lon = bbox.lon_min() - (bbox.lon_min() % step_lon);
let mut stop_lon = bbox.lon_max(); let mut stop_lon = bbox.lon_max();
@@ -221,7 +221,7 @@ impl ProjetedGrid {
let mut lon = start_lon; let mut lon = start_lon;
while lon < stop_lon { while lon < stop_lon {
if let Some(p) = if let Some(p) =
meridian::get_intersecting_meridian(lon, camera, projection, &self.fmt) meridian::get_intersecting_meridian(lon, camera, projection, self.fmt, decimal_lon_prec)
{ {
meridians.push(p); meridians.push(p);
} }
@@ -235,6 +235,8 @@ impl ProjetedGrid {
(bbox.get_lat_size() as f64) * step_line_px / (camera.get_height() as f64); (bbox.get_lat_size() as f64) * step_line_px / (camera.get_height() as f64);
let step_lat = select_fixed_step(step_lat_precised); let step_lat = select_fixed_step(step_lat_precised);
let decimal_lat_prec = step_lat.to_degrees().log10().abs().ceil() as u8;
let mut start_lat = bbox.lat_min() - (bbox.lat_min() % step_lat); let mut start_lat = bbox.lat_min() - (bbox.lat_min() % step_lat);
if start_lat == -HALF_PI { if start_lat == -HALF_PI {
start_lat += step_lat; start_lat += step_lat;
@@ -244,7 +246,7 @@ impl ProjetedGrid {
let mut parallels = vec![]; let mut parallels = vec![];
while lat < stop_lat { while lat < stop_lat {
if let Some(p) = parallel::get_intersecting_parallel(lat, camera, projection) { if let Some(p) = parallel::get_intersecting_parallel(lat, camera, projection, self.fmt, decimal_lat_prec) {
parallels.push(p); parallels.push(p);
} }
lat += step_lat; lat += step_lat;

View File

@@ -1,3 +1,5 @@
use al_api::angle::Formatter;
use super::label::Label; use super::label::Label;
use crate::math::projection::ProjectionType; use crate::math::projection::ProjectionType;
use crate::math::sph_geom::region::Intersection; use crate::math::sph_geom::region::Intersection;
@@ -6,15 +8,17 @@ use crate::CameraViewPort;
use crate::math::lonlat::LonLat; use crate::math::lonlat::LonLat;
use crate::math::{PI, TWICE_PI}; use crate::math::{PI, TWICE_PI};
use crate::renderable::line; use crate::renderable::line;
use core::ops::Range; use core::ops::Range;
pub fn get_intersecting_parallel( pub fn get_intersecting_parallel(
lat: f64, lat: f64,
camera: &CameraViewPort, camera: &CameraViewPort,
projection: &ProjectionType, projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
) -> Option<Parallel> { ) -> Option<Parallel> {
let fov = camera.get_field_of_view(); let fov = camera.get_field_of_view();
if fov.get_bounding_box().get_lon_size() > PI { if fov.get_bounding_box().get_lon_size() > PI {
@@ -28,6 +32,8 @@ pub fn get_intersecting_parallel(
camera, camera,
LabelOptions::Centered, LabelOptions::Centered,
projection, projection,
fmt,
grid_decimal_prec
)) ))
} else { } else {
// Longitude fov < PI // Longitude fov < PI
@@ -43,6 +49,8 @@ pub fn get_intersecting_parallel(
camera, camera,
LabelOptions::Centered, LabelOptions::Centered,
projection, projection,
fmt,
grid_decimal_prec
)) ))
} }
Intersection::Intersect { vertices } => { Intersection::Intersect { vertices } => {
@@ -65,6 +73,8 @@ pub fn get_intersecting_parallel(
camera, camera,
LabelOptions::OnSide, LabelOptions::OnSide,
projection, projection,
fmt,
grid_decimal_prec
)) ))
} }
Intersection::Empty => None, Intersection::Empty => None,
@@ -89,8 +99,10 @@ impl Parallel {
camera: &CameraViewPort, camera: &CameraViewPort,
label_options: LabelOptions, label_options: LabelOptions,
projection: &ProjectionType, projection: &ProjectionType,
fmt: Formatter,
grid_decimal_prec: u8
) -> Self { ) -> Self {
let label = Label::from_parallel(lat, lon, label_options, camera, projection); let label = Label::from_parallel(lat, lon, label_options, camera, projection, fmt, grid_decimal_prec);
// Draw the full parallel // Draw the full parallel
let vertices = if lon.end - lon.start > PI { let vertices = if lon.end - lon.start > PI {
@@ -109,24 +121,6 @@ impl Parallel {
line::parallel_arc::project(lat, lon.start, lon.end, camera, projection) line::parallel_arc::project(lat, lon.start, lon.end, camera, projection)
}; };
/*let mut prev_v = [vertices[0].x as f32, vertices[0].y as f32];
let vertices: Vec<_> = std::iter::once(prev_v)
.chain(
vertices.into_iter().skip(1)
.filter_map(|v| {
let cur_v = [v.x as f32, v.y as f32];
if cur_v == prev_v {
None
} else {
prev_v = cur_v;
Some(cur_v)
}
})
)
.collect();
let indices = vec![0..vertices.len()];
*/
let mut start_idx = 0; let mut start_idx = 0;
let mut indices = if vertices.len() >= 3 { let mut indices = if vertices.len() >= 3 {

View File

@@ -19,7 +19,6 @@ use al_core::VecData;
use al_core::VertexArrayObject; use al_core::VertexArrayObject;
use al_core::WebGlContext; use al_core::WebGlContext;
use crate::math::angle::Angle;
use crate::ProjectionType; use crate::ProjectionType;
use crate::camera::CameraViewPort; use crate::camera::CameraViewPort;
@@ -31,6 +30,8 @@ use crate::downloader::request::allsky::Allsky;
use crate::healpix::{cell::HEALPixCell, coverage::HEALPixCoverage}; use crate::healpix::{cell::HEALPixCell, coverage::HEALPixCoverage};
use crate::renderable::utils::index_patch::DefaultPatchIndexIter; use crate::renderable::utils::index_patch::DefaultPatchIndexIter;
use crate::time::Time; use crate::time::Time;
use crate::math::angle::ToAngle;
use super::config::HiPSConfig; use super::config::HiPSConfig;
use std::collections::HashSet; use std::collections::HashSet;
@@ -565,7 +566,7 @@ impl HiPS2D {
self.uv_end.extend(uv_end); self.uv_end.extend(uv_end);
self.time_tile_received.push(start_time); self.time_tile_received.push(start_time);
let xyz = crate::math::lonlat::radec_to_xyz(Angle(lon), Angle(lat)); let xyz = crate::math::lonlat::radec_to_xyz(lon.to_angle(), lat.to_angle());
pos.push([xyz.x as f32, xyz.y as f32, xyz.z as f32]); pos.push([xyz.x as f32, xyz.y as f32, xyz.z as f32]);
//pos.push([lon as f32, lat as f32]); //pos.push([lon as f32, lat as f32]);
} }

View File

@@ -1,19 +1,17 @@
use crate::camera::CameraViewPort; use crate::camera::CameraViewPort;
use crate::math::angle::Angle;
use crate::math::projection::ProjectionType; use crate::math::projection::ProjectionType;
use crate::math::vector::dist2; use crate::math::vector::dist2;
use crate::HEALPixCell; use crate::HEALPixCell;
use crate::math::angle::ToAngle;
const M: f64 = 280.0 * 280.0; const M: f64 = 280.0 * 280.0;
const N: f64 = 150.0 * 150.0; const N: f64 = 150.0 * 150.0;
const RAP: f64 = 0.7; const RAP: f64 = 0.7;
fn is_too_large(cell: &HEALPixCell, camera: &CameraViewPort, projection: &ProjectionType) -> bool { fn is_too_large(cell: &HEALPixCell, camera: &CameraViewPort, projection: &ProjectionType) -> bool {
let vertices = cell let vertices = cell
.vertices() .vertices()
.iter() .iter()
.filter_map(|(lon, lat)| { .filter_map(|(lon, lat)| {
let vertex = crate::math::lonlat::radec_to_xyzw(Angle(*lon), Angle(*lat)); let vertex = crate::math::lonlat::radec_to_xyzw(lon.to_angle(), lat.to_angle());
projection.icrs_celestial_to_screen_space(&vertex, camera) projection.icrs_celestial_to_screen_space(&vertex, camera)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View File

@@ -1416,6 +1416,12 @@ export let Aladin = (function () {
this.view.showCatalog(show); this.view.showCatalog(show);
}; };
/**
* Show/Hide the reticle centered on the view
*
* @memberof Aladin
* @param {boolean} show - True to enable, false to hide
*/
Aladin.prototype.showReticle = function (show) { Aladin.prototype.showReticle = function (show) {
this.reticle.update({ show }); this.reticle.update({ show });
}; };
@@ -1433,6 +1439,12 @@ export let Aladin = (function () {
}); });
}; };
/**
* Add a overlay of shapes/footprints to the view
*
* @memberof Aladin
* @param {GraphicOverlay} overlay - The shapes/footprints overlay to add
*/
Aladin.prototype.addOverlay = function (overlay) { Aladin.prototype.addOverlay = function (overlay) {
this.view.addOverlay(overlay); this.view.addOverlay(overlay);
@@ -1441,10 +1453,15 @@ export let Aladin = (function () {
}); });
}; };
/**
* Add a Multi-Order Coverage map (MOC) to the view
*
* @memberof Aladin
* @param {MOC} moc - The MOC object to add
*/
Aladin.prototype.addMOC = function (moc) { Aladin.prototype.addMOC = function (moc) {
this.view.addMOC(moc); this.view.addMOC(moc);
// see MOC.setView for sending it to outside the UI
}; };
Aladin.prototype.removeUIByName = function(name) { Aladin.prototype.removeUIByName = function(name) {
@@ -3078,12 +3095,5 @@ aladin.displayFITS(
}); });
}; };
/*
Aladin.prototype.setReduceDeformations = function (reduce) {
this.reduceDeformations = reduce;
this.view.requestRedraw();
}
*/
return Aladin; return Aladin;
})(); })();

View File

@@ -208,6 +208,7 @@ export let AladinUtils = {
* // returns "36 arcsec" * // returns "36 arcsec"
* Numbers.degreesToString(0.01); * Numbers.degreesToString(0.01);
*/ */
// FIXME: Same as in the libs/astro/angle.js
degreesToString: function(numberDegrees) { degreesToString: function(numberDegrees) {
let setPrecision = 3 let setPrecision = 3
let degrees = numberDegrees | 0; let degrees = numberDegrees | 0;

View File

@@ -1966,11 +1966,11 @@ export let View = (function () {
} }
// Set the grid label format // Set the grid label format
if (this.cooFrame.label == "ICRSd") { if (this.cooFrame.label == "ICRS") {
this.setGridOptions({fmt: "HMS"}); this.setGridOptions({fmt: "sexagesimal"});
} }
else { else {
this.setGridOptions({fmt: "DMS"}); this.setGridOptions({fmt: "decimal"});
} }
// Get the new view center position (given in icrs) // Get the new view center position (given in icrs)