mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2025-12-12 07:40:26 -08:00
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:
committed by
Matthieu Baumann
parent
ee2eb6e704
commit
ebb9d6d3d6
@@ -20,6 +20,7 @@
|
||||
showSettingsControl: true,
|
||||
showStackLayerControl: true,
|
||||
samp: true,
|
||||
showCooGrid: true,
|
||||
});
|
||||
|
||||
aladin.addCatalog(A.catalogFromSimbad('M 82', 0.1, {onClick: 'showTable'}));
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[wasm_bindgen(raw_module = "../../js/libs/astro/coo.js")]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_name = Format)]
|
||||
@@ -28,28 +26,11 @@ extern "C" {
|
||||
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]
|
||||
pub enum AngleSerializeFmt {
|
||||
DMM,
|
||||
DD,
|
||||
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)
|
||||
}
|
||||
}
|
||||
pub enum Formatter {
|
||||
Sexagesimal,
|
||||
Decimal
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::angle_fmt::AngleSerializeFmt;
|
||||
use crate::angle::Formatter;
|
||||
|
||||
use super::color::ColorRGB;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GridCfg {
|
||||
@@ -22,7 +19,7 @@ pub struct GridCfg {
|
||||
#[serde(default = "default_enabled")]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default = "default_fmt")]
|
||||
pub fmt: Option<AngleSerializeFmt>,
|
||||
pub fmt: Option<Formatter>,
|
||||
}
|
||||
|
||||
fn default_labels() -> Option<bool> {
|
||||
@@ -45,6 +42,6 @@ fn default_thickness() -> Option<f32> {
|
||||
None
|
||||
}
|
||||
|
||||
fn default_fmt() -> Option<AngleSerializeFmt> {
|
||||
fn default_fmt() -> Option<Formatter> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub mod resources;
|
||||
pub mod cell;
|
||||
pub mod fov;
|
||||
pub mod image;
|
||||
pub mod angle_fmt;
|
||||
pub mod angle;
|
||||
|
||||
pub trait Abort {
|
||||
type Item;
|
||||
|
||||
@@ -20,6 +20,7 @@ use crate::{
|
||||
tile_fetcher::TileFetcherQueue,
|
||||
time::DeltaTime,
|
||||
};
|
||||
use crate::math::angle::ToAngle;
|
||||
use al_core::image::format::ChannelType;
|
||||
use wcs::WCS;
|
||||
|
||||
@@ -421,7 +422,7 @@ impl App {
|
||||
let v = cell.vertices();
|
||||
let proj2screen = |(lon, lat): &(f64, f64)| -> Option<[f64; 2]> {
|
||||
// 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
|
||||
let xyzw = crate::coosys::apply_coo_system(
|
||||
CooSystem::ICRS,
|
||||
@@ -1325,7 +1326,7 @@ impl App {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_max_fov(&self) -> f64 {
|
||||
pub(crate) fn get_max_fov(&self) -> Angle<f64> {
|
||||
self.projection.aperture_start()
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn build_fov_coverage(
|
||||
|
||||
let hpx_idxs_iter = vertices_iter.map(|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()))
|
||||
|
||||
@@ -88,7 +88,6 @@ const MAX_DPI_LIMIT: f32 = 2.0;
|
||||
use crate::math;
|
||||
use crate::time::Time;
|
||||
use crate::Abort;
|
||||
use crate::ArcDeg;
|
||||
impl CameraViewPort {
|
||||
pub fn new(
|
||||
gl: &WebGlContext,
|
||||
@@ -97,7 +96,7 @@ impl CameraViewPort {
|
||||
) -> CameraViewPort {
|
||||
let last_user_action = UserAction::Starting;
|
||||
|
||||
let aperture = Angle(projection.aperture_start());
|
||||
let aperture = projection.aperture_start();
|
||||
|
||||
let w2m = Matrix4::identity();
|
||||
let m2w = w2m;
|
||||
@@ -350,12 +349,12 @@ impl CameraViewPort {
|
||||
_ => 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 {
|
||||
//al_core::log(&format!("a: {:?}, as: {:?}", aperture, aperture_start));
|
||||
if can_unzoom_more {
|
||||
aperture.0 / aperture_start.0
|
||||
aperture.to_radians() / aperture_start.to_radians()
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
@@ -363,8 +362,8 @@ impl CameraViewPort {
|
||||
// Compute the new clip zoom factor
|
||||
let a = aperture.abs();
|
||||
|
||||
let v0 = math::lonlat::radec_to_xyzw(-a / 2.0, Angle(0.0));
|
||||
let v1 = 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, 0.0.to_angle());
|
||||
|
||||
// Vertex in the WCS of the FOV
|
||||
if self.width < self.height {
|
||||
|
||||
@@ -39,8 +39,8 @@ mod tests {
|
||||
let gal_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_lat_deg = gal_lonlat.lat().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().to_degrees();
|
||||
|
||||
assert_delta!(gal_lon_deg, 96.33723581, 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 j2000_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_lat_deg = j2000_lonlat.lat().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().to_degrees();
|
||||
|
||||
assert_delta!(j2000_lon_deg, 266.40506655, 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_lon_deg = gal_lonlat.lon().0 * 360.0 / (2.0 * std::f64::consts::PI);
|
||||
let gal_lat_deg = gal_lonlat.lat().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().to_degrees();
|
||||
|
||||
assert_delta!(gal_lon_deg, 0.0, 1e-3);
|
||||
assert_delta!(gal_lat_deg, 0.0, 1e-3);
|
||||
|
||||
@@ -27,15 +27,14 @@ impl HEALPixCoverage {
|
||||
let lonlat = vertices_iter
|
||||
.map(|vertex| {
|
||||
let LonLatT(lon, lat) = vertex.lonlat();
|
||||
//let (lon, lat) = math::lonlat::xyzw_to_radec(&vertex);
|
||||
(lon.0, lat.0)
|
||||
(lon.to_radians(), lat.to_radians())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let LonLatT(in_lon, in_lat) = inside.lonlat();
|
||||
let moc = RangeMOC::from_polygon_with_control_point(
|
||||
&lonlat[..],
|
||||
(in_lon.0, in_lat.0),
|
||||
(in_lon.to_radians(), in_lat.to_radians()),
|
||||
depth,
|
||||
CellSelection::All,
|
||||
);
|
||||
@@ -84,11 +83,11 @@ impl HEALPixCoverage {
|
||||
|
||||
pub fn contains_coo(&self, coo: &Vector4<f64>) -> bool {
|
||||
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 {
|
||||
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))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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
|
||||
/// 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 lat = S::from(*lat).unwrap_abort();
|
||||
|
||||
(lon, lat)
|
||||
(lon.to_angle(), lat.to_angle())
|
||||
})
|
||||
.unzip();
|
||||
|
||||
[
|
||||
LonLatT::new(Angle(lon[0]), Angle(lat[0])),
|
||||
LonLatT::new(Angle(lon[1]), Angle(lat[1])),
|
||||
LonLatT::new(Angle(lon[2]), Angle(lat[2])),
|
||||
LonLatT::new(Angle(lon[3]), Angle(lat[3])),
|
||||
LonLatT::new(lon[0], lat[0]),
|
||||
LonLatT::new(lon[1], lat[1]),
|
||||
LonLatT::new(lon[2], lat[2]),
|
||||
LonLatT::new(lon[3], lat[3]),
|
||||
]
|
||||
}
|
||||
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 lat = S::from(*lat).unwrap_abort();
|
||||
|
||||
LonLatT::new(Angle(lon), Angle(lat))
|
||||
LonLatT::new(lon.to_angle(), lat.to_angle())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
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] = &[
|
||||
|
||||
@@ -562,7 +562,7 @@ impl WebClient {
|
||||
/// the sinus would be 180 degrees.
|
||||
#[wasm_bindgen(js_name = getMaxFieldOfView)]
|
||||
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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use cgmath::BaseFloat;
|
||||
use crate::Abort;
|
||||
// ArcDeg wrapper structure
|
||||
#[derive(Clone, Copy)]
|
||||
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 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 serde::Deserialize;
|
||||
// Convert a Rad<T> to an ArcDeg<T>
|
||||
@@ -71,18 +55,6 @@ where
|
||||
#[derive(Clone, Copy)]
|
||||
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>
|
||||
where
|
||||
@@ -122,19 +94,6 @@ where
|
||||
#[derive(Clone, Copy)]
|
||||
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>
|
||||
impl<T> From<Rad<T>> for ArcMin<T>
|
||||
where
|
||||
@@ -241,6 +200,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
use al_api::angle::Format;
|
||||
/*
|
||||
pub enum SerializeFmt {
|
||||
DMS,
|
||||
HMS,
|
||||
@@ -269,7 +230,7 @@ impl SerializeFmt {
|
||||
Self::DD => DD::to_string(angle),
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/*pub trait SerializeToString {
|
||||
fn to_string(&self) -> String;
|
||||
@@ -284,6 +245,7 @@ where
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
pub struct DMS;
|
||||
pub struct HMS;
|
||||
pub struct DMM;
|
||||
@@ -315,7 +277,7 @@ impl FormatType for DMM {
|
||||
result
|
||||
}
|
||||
}
|
||||
use crate::Abort;
|
||||
|
||||
impl FormatType for DMS {
|
||||
fn to_string<S: BaseFloat + ToString>(angle: Angle<S>) -> String {
|
||||
let angle = Rad(angle.0);
|
||||
@@ -361,94 +323,106 @@ impl FormatType for HMS {
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[repr(C)]
|
||||
pub struct Angle<S: BaseFloat>(pub S);
|
||||
pub struct Angle<S: BaseFloat> {
|
||||
pub rad: S,
|
||||
fmt: AngleFormatter,
|
||||
}
|
||||
impl<S> Angle<S>
|
||||
where
|
||||
S: BaseFloat,
|
||||
{
|
||||
pub fn new<T: Into<Rad<S>>>(angle: T) -> Angle<S> {
|
||||
let radians: Rad<S> = angle.into();
|
||||
Angle(radians.0)
|
||||
Angle { rad: radians.0, fmt: AngleFormatter::default() }
|
||||
}
|
||||
|
||||
pub fn cos(&self) -> S {
|
||||
self.0.cos()
|
||||
self.rad.cos()
|
||||
}
|
||||
|
||||
pub fn sin(&self) -> S {
|
||||
self.0.sin()
|
||||
self.rad.sin()
|
||||
}
|
||||
|
||||
pub fn tan(&self) -> S {
|
||||
self.0.tan()
|
||||
self.rad.tan()
|
||||
}
|
||||
|
||||
pub fn asin(self) -> S {
|
||||
self.0.asin()
|
||||
self.rad.asin()
|
||||
}
|
||||
|
||||
pub fn acos(self) -> S {
|
||||
self.0.acos()
|
||||
self.rad.acos()
|
||||
}
|
||||
|
||||
pub fn atan(self) -> S {
|
||||
self.0.atan()
|
||||
self.rad.atan()
|
||||
}
|
||||
|
||||
pub fn atan2(self, other: Self) -> S {
|
||||
self.0.atan2(other.0)
|
||||
self.rad.atan2(other.rad)
|
||||
}
|
||||
|
||||
pub fn floor(self) -> Self {
|
||||
Angle(self.0.floor())
|
||||
self.rad.floor().to_angle()
|
||||
}
|
||||
|
||||
pub fn ceil(self) -> Self {
|
||||
Angle(self.0.ceil())
|
||||
self.rad.ceil().to_angle()
|
||||
}
|
||||
|
||||
pub fn round(self) -> Self {
|
||||
Angle(self.0.round())
|
||||
self.rad.round().to_angle()
|
||||
}
|
||||
|
||||
pub fn trunc(self) -> Self {
|
||||
Angle(self.0.trunc())
|
||||
self.rad.trunc().to_angle()
|
||||
}
|
||||
|
||||
pub fn fract(self) -> S {
|
||||
self.0.fract()
|
||||
self.rad.fract()
|
||||
}
|
||||
|
||||
pub fn abs(self) -> Self {
|
||||
Angle(self.0.abs())
|
||||
self.rad.abs().to_angle()
|
||||
}
|
||||
|
||||
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 {
|
||||
Angle(self.0.min(other.0))
|
||||
self.rad.min(other.rad).to_angle()
|
||||
}
|
||||
|
||||
pub fn min_value() -> Self {
|
||||
Angle(S::min_value())
|
||||
S::min_value().to_angle()
|
||||
}
|
||||
pub fn max_value() -> Self {
|
||||
Angle(S::max_value())
|
||||
S::max_value().to_angle()
|
||||
}
|
||||
|
||||
pub fn to_radians(&self) -> S {
|
||||
self.0
|
||||
self.rad
|
||||
}
|
||||
|
||||
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,
|
||||
{
|
||||
fn to_angle(self) -> Angle<S> {
|
||||
Angle(self)
|
||||
Angle { rad: self, fmt: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +448,7 @@ where
|
||||
S: BaseFloat,
|
||||
{
|
||||
fn from(rad: Rad<S>) -> Self {
|
||||
Angle(rad.0)
|
||||
rad.0.to_angle()
|
||||
}
|
||||
}
|
||||
impl<S> From<Angle<S>> for Rad<S>
|
||||
@@ -482,7 +456,7 @@ where
|
||||
S: BaseFloat,
|
||||
{
|
||||
fn from(angle: Angle<S>) -> Self {
|
||||
Rad(angle.0)
|
||||
Rad(angle.rad)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +475,7 @@ where
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
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> {
|
||||
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 {
|
||||
let rad: Rad<S> = deg.into();
|
||||
Angle(rad.0)
|
||||
rad.0.to_angle()
|
||||
}
|
||||
}
|
||||
impl<S> From<Angle<S>> for ArcDeg<S>
|
||||
@@ -545,7 +519,7 @@ where
|
||||
{
|
||||
fn from(min: ArcMin<S>) -> Self {
|
||||
let rad: Rad<S> = min.into();
|
||||
Angle(rad.0)
|
||||
rad.0.to_angle()
|
||||
}
|
||||
}
|
||||
// Convert from ArcSec<S>
|
||||
@@ -555,28 +529,11 @@ where
|
||||
{
|
||||
fn from(sec: ArcSec<S>) -> Self {
|
||||
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;
|
||||
/*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;
|
||||
impl<S> Div for Angle<S>
|
||||
@@ -586,8 +543,8 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
let angle = self.0 / rhs.0;
|
||||
Angle(angle)
|
||||
let rad = self.rad / rhs.rad;
|
||||
rad.to_angle()
|
||||
}
|
||||
}
|
||||
impl<S> Div<S> for Angle<S>
|
||||
@@ -597,8 +554,8 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: S) -> Self::Output {
|
||||
let angle = self.0 / rhs;
|
||||
Angle(angle)
|
||||
let rad = self.rad / rhs;
|
||||
rad.to_angle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,8 +567,8 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
let angle = self.0 * rhs.0;
|
||||
Angle(angle)
|
||||
let angle = self.rad * rhs.rad;
|
||||
angle.to_angle()
|
||||
}
|
||||
}
|
||||
impl<S> Mul<S> for Angle<S>
|
||||
@@ -621,8 +578,8 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: S) -> Self::Output {
|
||||
let angle = self.0 * rhs;
|
||||
Angle(angle)
|
||||
let angle = self.rad * rhs;
|
||||
angle.to_angle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,8 +591,8 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: Self) -> Self::Output {
|
||||
let angle = self.0 - other.0;
|
||||
Angle(angle)
|
||||
let angle = self.rad - other.rad;
|
||||
angle.to_angle()
|
||||
}
|
||||
}
|
||||
impl<S> Sub<S> for Angle<S>
|
||||
@@ -645,8 +602,8 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: S) -> Self::Output {
|
||||
let angle = self.0 - other;
|
||||
Angle(angle)
|
||||
let angle = self.rad - other;
|
||||
angle.to_angle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,8 +615,8 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self::Output {
|
||||
let angle = self.0 + other.0;
|
||||
Angle(angle)
|
||||
let angle = self.rad + other.rad;
|
||||
angle.to_angle()
|
||||
}
|
||||
}
|
||||
impl<S> Add<S> for Angle<S>
|
||||
@@ -669,8 +626,8 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: S) -> Self::Output {
|
||||
let angle = self.0 + other;
|
||||
Angle(angle)
|
||||
let angle = self.rad + other;
|
||||
angle.to_angle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,8 +675,56 @@ where
|
||||
type Output = Self;
|
||||
|
||||
fn rem(self, other: Self) -> Self::Output {
|
||||
let angle = self.0 % other.0;
|
||||
Angle(angle)
|
||||
let angle = self.rad % other.rad;
|
||||
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;
|
||||
fn neg(self) -> Self::Output {
|
||||
Angle(-self.0)
|
||||
(-self.rad).to_angle()
|
||||
}
|
||||
}
|
||||
use al_core::{shader::UniformType, WebGlContext};
|
||||
use web_sys::WebGlUniformLocation;
|
||||
impl UniformType for Angle<f32> {
|
||||
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
|
||||
gl.uniform1f(location, value.0);
|
||||
gl.uniform1f(location, value.rad);
|
||||
}
|
||||
}
|
||||
impl UniformType for Angle<f64> {
|
||||
fn uniform(gl: &WebGlContext, location: Option<&WebGlUniformLocation>, value: &Self) {
|
||||
gl.uniform1f(location, value.0 as f32);
|
||||
gl.uniform1f(location, value.rad as f32);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ where
|
||||
/// * ``lon`` - Longitude
|
||||
/// * ``lat`` - Latitude
|
||||
pub fn new(mut lon: Angle<S>, lat: Angle<S>) -> LonLatT<S> {
|
||||
if lon.0 < S::zero() {
|
||||
lon.0 = lon.0 + S::from(TWICE_PI).unwrap_abort();
|
||||
if lon.to_radians() < S::zero() {
|
||||
lon = lon + S::from(TWICE_PI).unwrap_abort();
|
||||
}
|
||||
|
||||
LonLatT(lon, lat)
|
||||
@@ -156,33 +156,31 @@ where
|
||||
#[inline]
|
||||
pub fn ang_between_lonlat<S: BaseFloat>(lonlat1: LonLatT<S>, lonlat2: LonLatT<S>) -> Angle<S> {
|
||||
let abs_diff_lon = (lonlat1.lon() - lonlat2.lon()).abs();
|
||||
Angle(
|
||||
(lonlat1.lat().sin() * lonlat2.lat().sin()
|
||||
+ lonlat1.lat().cos() * lonlat2.lat().cos() * abs_diff_lon.cos())
|
||||
.acos(),
|
||||
)
|
||||
(lonlat1.lat().sin() * lonlat2.lat().sin()
|
||||
+ lonlat1.lat().cos() * lonlat2.lat().cos() * abs_diff_lon.cos())
|
||||
.acos().to_angle()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn xyz_to_radec<S: BaseFloat>(v: &Vector3<S>) -> (Angle<S>, Angle<S>) {
|
||||
let lon = Angle(v.x.atan2(v.z));
|
||||
let lat = Angle(v.y.atan2((v.x * v.x + v.z * v.z).sqrt()));
|
||||
let lon = (v.x.atan2(v.z)).to_angle();
|
||||
let lat = (v.y.atan2((v.x * v.x + v.z * v.z).sqrt())).to_angle();
|
||||
|
||||
(lon, lat)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn xyzw_to_radec<S: BaseFloat>(v: &Vector4<S>) -> (Angle<S>, Angle<S>) {
|
||||
let lon = Angle(v.x.atan2(v.z));
|
||||
let lat = Angle(v.y.atan2((v.x * v.x + v.z * v.z).sqrt()));
|
||||
let lon = (v.x.atan2(v.z)).to_angle();
|
||||
let lat = (v.y.atan2((v.x * v.x + v.z * v.z).sqrt())).to_angle();
|
||||
|
||||
(lon, lat)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn radec_to_xyz<S: BaseFloat>(theta: Angle<S>, delta: Angle<S>) -> Vector3<S> {
|
||||
let (ds, dc) = delta.0.sin_cos();
|
||||
let (ts, tc) = theta.0.sin_cos();
|
||||
let (ds, dc) = delta.to_radians().sin_cos();
|
||||
let (ts, tc) = theta.to_radians().sin_cos();
|
||||
|
||||
Vector3::<S>::new(dc * ts, ds, dc * tc)
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ use cgmath::Vector2;
|
||||
pub mod coo_space;
|
||||
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 */
|
||||
pub fn screen_to_ndc_space(
|
||||
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 {
|
||||
// Zenithal projections
|
||||
/* TAN, Gnomonic projection */
|
||||
@@ -355,17 +357,17 @@ impl ProjectionType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aperture_start(&self) -> f64 {
|
||||
pub fn aperture_start(&self) -> Angle<f64> {
|
||||
match self {
|
||||
// Zenithal projections
|
||||
/* TAN, Gnomonic projection */
|
||||
ProjectionType::Tan(_) => 150.0,
|
||||
ProjectionType::Tan(_) => 150.0_f64.to_radians().to_angle(),
|
||||
/* STG, Stereographic projection */
|
||||
ProjectionType::Stg(_) => 360.0,
|
||||
ProjectionType::Stg(_) => 360.0_f64.to_radians().to_angle(),
|
||||
/* SIN, Orthographic */
|
||||
ProjectionType::Sin(_) => 180.0,
|
||||
ProjectionType::Sin(_) => 180.0_f64.to_radians().to_angle(),
|
||||
/* ZEA, Equal-area */
|
||||
ProjectionType::Zea(_) => 360.0,
|
||||
ProjectionType::Zea(_) => 360.0_f64.to_radians().to_angle(),
|
||||
/* FEYE, Fish-eyes */
|
||||
//ProjectionType::Feye(_) => 190.0,
|
||||
/* AIR, */
|
||||
@@ -379,9 +381,9 @@ impl ProjectionType {
|
||||
|
||||
// Pseudo-cylindrical projections
|
||||
/* AIT, Aitoff */
|
||||
ProjectionType::Ait(_) => 360.0,
|
||||
ProjectionType::Ait(_) => 360.0_f64.to_radians().to_angle(),
|
||||
// MOL, Mollweide */
|
||||
ProjectionType::Mol(_) => 360.0,
|
||||
ProjectionType::Mol(_) => 360.0_f64.to_radians().to_angle(),
|
||||
// PAR, */
|
||||
//ProjectionType::Par(_) => 360.0,
|
||||
// SFL, */
|
||||
@@ -389,7 +391,7 @@ impl ProjectionType {
|
||||
|
||||
// Cylindrical projections
|
||||
// MER, Mercator */
|
||||
ProjectionType::Mer(_) => 360.0,
|
||||
ProjectionType::Mer(_) => 360.0_f64.to_radians().to_angle(),
|
||||
// CAR, */
|
||||
//ProjectionType::Car(_) => 360.0,
|
||||
// CEA, */
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::math;
|
||||
use cgmath::{BaseFloat, InnerSpace};
|
||||
use cgmath::{Euler, Quaternion};
|
||||
use cgmath::{Vector3, Vector4};
|
||||
use crate::math::angle::ToAngle;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
// Internal structure of a rotation, a quaternion
|
||||
@@ -159,7 +160,7 @@ where
|
||||
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 c = m.x.y.atan2(m.y.y);
|
||||
(Angle(a), Angle(b), Angle(c))
|
||||
(a.to_angle(), b.to_angle(), c.to_angle())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use crate::math::angle::Angle;
|
||||
use cgmath::{BaseFloat, InnerSpace, Vector2, Vector3};
|
||||
use crate::math::angle::ToAngle;
|
||||
|
||||
#[inline]
|
||||
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]
|
||||
pub fn angle3<S: BaseFloat>(x: &Vector3<S>, y: &cgmath::Vector3<S>) -> Angle<S> {
|
||||
let theta = x.cross(*y).magnitude().atan2(x.dot(*y));
|
||||
Angle(theta)
|
||||
theta.to_angle()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -6,12 +6,13 @@ use crate::ProjectionType;
|
||||
use cgmath::InnerSpace;
|
||||
use cgmath::Vector3;
|
||||
|
||||
use crate::math::angle::SerializeFmt;
|
||||
use crate::math::lonlat::LonLat;
|
||||
use crate::math::projection::coo_space::XYScreen;
|
||||
use crate::math::TWICE_PI;
|
||||
|
||||
use crate::math::angle::ToAngle;
|
||||
use crate::math::angle::AngleFormatter;
|
||||
use al_api::angle::Formatter;
|
||||
use cgmath::Vector2;
|
||||
use core::ops::Range;
|
||||
|
||||
@@ -22,7 +23,6 @@ pub enum LabelOptions {
|
||||
Centered,
|
||||
OnSide,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Label {
|
||||
// The position
|
||||
@@ -39,7 +39,8 @@ impl Label {
|
||||
options: LabelOptions,
|
||||
camera: &CameraViewPort,
|
||||
projection: &ProjectionType,
|
||||
_fmt: &SerializeFmt,
|
||||
fmt: Formatter,
|
||||
grid_decimal_prec: u8
|
||||
) -> Option<Self> {
|
||||
let fov = camera.get_field_of_view();
|
||||
let d = if fov.contains_north_pole() {
|
||||
@@ -76,8 +77,18 @@ impl Label {
|
||||
lon += TWICE_PI;
|
||||
}
|
||||
|
||||
//let content = fmt.to_string(lon.to_angle());
|
||||
let content = al_api::angle_fmt::Format::toSexagesimal(lon.to_degrees() / 15.0, 8, false);
|
||||
let mut angle = lon.to_angle();
|
||||
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() {
|
||||
d1 + OFF_TANGENT * dt - OFF_BI_TANGENT * db
|
||||
@@ -101,6 +112,8 @@ impl Label {
|
||||
options: LabelOptions,
|
||||
camera: &CameraViewPort,
|
||||
projection: &ProjectionType,
|
||||
fmt: Formatter,
|
||||
grid_decimal_prec: u8
|
||||
) -> Option<Self> {
|
||||
let lonlat = match options {
|
||||
LabelOptions::Centered => {
|
||||
@@ -130,8 +143,18 @@ impl Label {
|
||||
let dt = (d2 - d1).normalize();
|
||||
let db = Vector2::new(dt.y.abs(), dt.x.abs());
|
||||
|
||||
//let content = SerializeFmt::DMS.to_string(lonlat.lat());
|
||||
let content = al_api::angle_fmt::Format::toSexagesimal(lonlat.lat().to_degrees(), 7, false);
|
||||
let mut angle = lat.to_angle();
|
||||
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 position = if !fov.is_allsky() && !fov.contains_pole() {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use al_api::angle::Formatter;
|
||||
|
||||
use super::label::{Label, LabelOptions};
|
||||
use crate::math::lonlat::LonLat;
|
||||
use crate::math::sph_geom::region::Intersection;
|
||||
@@ -7,14 +9,14 @@ use core::ops::Range;
|
||||
use crate::math::MINUS_HALF_PI;
|
||||
use crate::ProjectionType;
|
||||
|
||||
use super::angle::SerializeFmt;
|
||||
use crate::math::HALF_PI;
|
||||
|
||||
pub fn get_intersecting_meridian(
|
||||
lon: f64,
|
||||
camera: &CameraViewPort,
|
||||
projection: &ProjectionType,
|
||||
fmt: &SerializeFmt,
|
||||
fmt: Formatter,
|
||||
grid_decimal_prec: u8
|
||||
) -> Option<Meridian> {
|
||||
let fov = camera.get_field_of_view();
|
||||
if fov.contains_both_poles() {
|
||||
@@ -25,6 +27,7 @@ pub fn get_intersecting_meridian(
|
||||
camera,
|
||||
projection,
|
||||
fmt,
|
||||
grid_decimal_prec
|
||||
);
|
||||
Some(meridian)
|
||||
} else {
|
||||
@@ -39,6 +42,7 @@ pub fn get_intersecting_meridian(
|
||||
camera,
|
||||
projection,
|
||||
fmt,
|
||||
grid_decimal_prec
|
||||
);
|
||||
Some(meridian)
|
||||
}
|
||||
@@ -56,7 +60,7 @@ pub fn get_intersecting_meridian(
|
||||
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 => {
|
||||
// full intersection
|
||||
@@ -73,56 +77,18 @@ pub fn get_intersecting_meridian(
|
||||
camera,
|
||||
projection,
|
||||
fmt,
|
||||
grid_decimal_prec
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
/*let mut vertices = vertices.into_vec();
|
||||
// One segment over two will be in the field of view
|
||||
vertices.push(Vector4::new(0.0, 1.0, 0.0, 1.0));
|
||||
vertices.push(Vector4::new(0.0, -1.0, 0.0, 1.0));
|
||||
|
||||
vertices.sort_by(|i1, i2| {
|
||||
i1.y.total_cmp(&i2.y)
|
||||
});
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
_ => Meridian::new(
|
||||
lon,
|
||||
&(-HALF_PI..HALF_PI),
|
||||
LabelOptions::OnSide,
|
||||
camera,
|
||||
projection,
|
||||
fmt,
|
||||
grid_decimal_prec
|
||||
)
|
||||
};
|
||||
|
||||
Some(meridian)
|
||||
@@ -139,7 +105,6 @@ pub struct Meridian {
|
||||
indices: Vec<Range<usize>>,
|
||||
label: Option<Label>,
|
||||
}
|
||||
|
||||
impl Meridian {
|
||||
pub fn new(
|
||||
lon: f64,
|
||||
@@ -147,9 +112,10 @@ impl Meridian {
|
||||
label_options: LabelOptions,
|
||||
camera: &CameraViewPort,
|
||||
projection: &ProjectionType,
|
||||
fmt: &SerializeFmt,
|
||||
fmt: Formatter,
|
||||
grid_decimal_prec: u8
|
||||
) -> 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
|
||||
let vertices = crate::renderable::line::great_circle_arc::project(
|
||||
@@ -185,52 +151,6 @@ impl Meridian {
|
||||
|
||||
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 {
|
||||
vertices,
|
||||
indices,
|
||||
|
||||
@@ -8,7 +8,6 @@ use al_core::VecData;
|
||||
use parallel::Parallel;
|
||||
|
||||
use crate::camera::CameraViewPort;
|
||||
use crate::math::angle;
|
||||
use crate::math::HALF_PI;
|
||||
use crate::ProjectionType;
|
||||
use al_api::color::ColorRGBA;
|
||||
@@ -28,7 +27,7 @@ pub struct ProjetedGrid {
|
||||
|
||||
// Render Text Manager
|
||||
text_renderer: TextRenderManager,
|
||||
fmt: angle::SerializeFmt,
|
||||
fmt: Formatter,
|
||||
|
||||
//line_style: line::Style,
|
||||
meridians: Vec<Meridian>,
|
||||
@@ -41,9 +40,8 @@ use crate::renderable::text::TextRenderManager;
|
||||
use crate::renderable::Renderer;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::HtmlElement;
|
||||
|
||||
use al_api::angle::Formatter;
|
||||
use self::meridian::Meridian;
|
||||
|
||||
impl ProjetedGrid {
|
||||
pub fn new(gl: WebGlContext, aladin_div: &HtmlElement) -> Result<ProjetedGrid, JsValue> {
|
||||
let text_renderer = TextRenderManager::new(aladin_div)?;
|
||||
@@ -58,7 +56,7 @@ impl ProjetedGrid {
|
||||
let enabled = false;
|
||||
let label_scale = 1.0;
|
||||
//let line_style = line::Style::None;
|
||||
let fmt = angle::SerializeFmt::DMS;
|
||||
let fmt = Formatter::Decimal;
|
||||
let thickness = 2.0;
|
||||
let meridians = Vec::new();
|
||||
let parallels = Vec::new();
|
||||
@@ -150,7 +148,7 @@ impl ProjetedGrid {
|
||||
}
|
||||
|
||||
if let Some(fmt) = fmt {
|
||||
self.fmt = fmt.into();
|
||||
self.fmt = fmt;
|
||||
}
|
||||
|
||||
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);
|
||||
let step_lon = select_fixed_step(step_lon_precised);
|
||||
|
||||
let decimal_lon_prec = step_lon.to_degrees().log10().abs().ceil() as u8;
|
||||
|
||||
// Add meridians
|
||||
let start_lon = bbox.lon_min() - (bbox.lon_min() % step_lon);
|
||||
let mut stop_lon = bbox.lon_max();
|
||||
@@ -221,7 +221,7 @@ impl ProjetedGrid {
|
||||
let mut lon = start_lon;
|
||||
while lon < stop_lon {
|
||||
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);
|
||||
}
|
||||
@@ -235,6 +235,8 @@ impl ProjetedGrid {
|
||||
(bbox.get_lat_size() as f64) * step_line_px / (camera.get_height() as f64);
|
||||
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);
|
||||
if start_lat == -HALF_PI {
|
||||
start_lat += step_lat;
|
||||
@@ -244,7 +246,7 @@ impl ProjetedGrid {
|
||||
|
||||
let mut parallels = vec![];
|
||||
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);
|
||||
}
|
||||
lat += step_lat;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use al_api::angle::Formatter;
|
||||
|
||||
use super::label::Label;
|
||||
use crate::math::projection::ProjectionType;
|
||||
use crate::math::sph_geom::region::Intersection;
|
||||
@@ -6,15 +8,17 @@ use crate::CameraViewPort;
|
||||
use crate::math::lonlat::LonLat;
|
||||
use crate::math::{PI, TWICE_PI};
|
||||
|
||||
|
||||
use crate::renderable::line;
|
||||
|
||||
use core::ops::Range;
|
||||
|
||||
|
||||
pub fn get_intersecting_parallel(
|
||||
lat: f64,
|
||||
camera: &CameraViewPort,
|
||||
projection: &ProjectionType,
|
||||
fmt: Formatter,
|
||||
grid_decimal_prec: u8
|
||||
) -> Option<Parallel> {
|
||||
let fov = camera.get_field_of_view();
|
||||
if fov.get_bounding_box().get_lon_size() > PI {
|
||||
@@ -28,6 +32,8 @@ pub fn get_intersecting_parallel(
|
||||
camera,
|
||||
LabelOptions::Centered,
|
||||
projection,
|
||||
fmt,
|
||||
grid_decimal_prec
|
||||
))
|
||||
} else {
|
||||
// Longitude fov < PI
|
||||
@@ -43,6 +49,8 @@ pub fn get_intersecting_parallel(
|
||||
camera,
|
||||
LabelOptions::Centered,
|
||||
projection,
|
||||
fmt,
|
||||
grid_decimal_prec
|
||||
))
|
||||
}
|
||||
Intersection::Intersect { vertices } => {
|
||||
@@ -65,6 +73,8 @@ pub fn get_intersecting_parallel(
|
||||
camera,
|
||||
LabelOptions::OnSide,
|
||||
projection,
|
||||
fmt,
|
||||
grid_decimal_prec
|
||||
))
|
||||
}
|
||||
Intersection::Empty => None,
|
||||
@@ -89,8 +99,10 @@ impl Parallel {
|
||||
camera: &CameraViewPort,
|
||||
label_options: LabelOptions,
|
||||
projection: &ProjectionType,
|
||||
fmt: Formatter,
|
||||
grid_decimal_prec: u8
|
||||
) -> 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
|
||||
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)
|
||||
};
|
||||
|
||||
/*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 indices = if vertices.len() >= 3 {
|
||||
|
||||
@@ -19,7 +19,6 @@ use al_core::VecData;
|
||||
use al_core::VertexArrayObject;
|
||||
use al_core::WebGlContext;
|
||||
|
||||
use crate::math::angle::Angle;
|
||||
use crate::ProjectionType;
|
||||
|
||||
use crate::camera::CameraViewPort;
|
||||
@@ -31,6 +30,8 @@ use crate::downloader::request::allsky::Allsky;
|
||||
use crate::healpix::{cell::HEALPixCell, coverage::HEALPixCoverage};
|
||||
use crate::renderable::utils::index_patch::DefaultPatchIndexIter;
|
||||
use crate::time::Time;
|
||||
use crate::math::angle::ToAngle;
|
||||
|
||||
|
||||
use super::config::HiPSConfig;
|
||||
use std::collections::HashSet;
|
||||
@@ -565,7 +566,7 @@ impl HiPS2D {
|
||||
self.uv_end.extend(uv_end);
|
||||
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([lon as f32, lat as f32]);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
use crate::camera::CameraViewPort;
|
||||
use crate::math::angle::Angle;
|
||||
use crate::math::projection::ProjectionType;
|
||||
use crate::math::vector::dist2;
|
||||
use crate::HEALPixCell;
|
||||
|
||||
use crate::math::angle::ToAngle;
|
||||
const M: f64 = 280.0 * 280.0;
|
||||
const N: f64 = 150.0 * 150.0;
|
||||
const RAP: f64 = 0.7;
|
||||
|
||||
fn is_too_large(cell: &HEALPixCell, camera: &CameraViewPort, projection: &ProjectionType) -> bool {
|
||||
let vertices = cell
|
||||
.vertices()
|
||||
.iter()
|
||||
.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)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -1416,6 +1416,12 @@ export let Aladin = (function () {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
this.view.addMOC(moc);
|
||||
|
||||
// see MOC.setView for sending it to outside the UI
|
||||
};
|
||||
|
||||
Aladin.prototype.removeUIByName = function(name) {
|
||||
@@ -3078,12 +3095,5 @@ aladin.displayFITS(
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Aladin.prototype.setReduceDeformations = function (reduce) {
|
||||
this.reduceDeformations = reduce;
|
||||
this.view.requestRedraw();
|
||||
}
|
||||
*/
|
||||
|
||||
return Aladin;
|
||||
})();
|
||||
|
||||
@@ -208,6 +208,7 @@ export let AladinUtils = {
|
||||
* // returns "36 arcsec"
|
||||
* Numbers.degreesToString(0.01);
|
||||
*/
|
||||
// FIXME: Same as in the libs/astro/angle.js
|
||||
degreesToString: function(numberDegrees) {
|
||||
let setPrecision = 3
|
||||
let degrees = numberDegrees | 0;
|
||||
|
||||
@@ -1966,11 +1966,11 @@ export let View = (function () {
|
||||
}
|
||||
|
||||
// Set the grid label format
|
||||
if (this.cooFrame.label == "ICRSd") {
|
||||
this.setGridOptions({fmt: "HMS"});
|
||||
if (this.cooFrame.label == "ICRS") {
|
||||
this.setGridOptions({fmt: "sexagesimal"});
|
||||
}
|
||||
else {
|
||||
this.setGridOptions({fmt: "DMS"});
|
||||
this.setGridOptions({fmt: "decimal"});
|
||||
}
|
||||
|
||||
// Get the new view center position (given in icrs)
|
||||
|
||||
Reference in New Issue
Block a user