From 4ee5d0ee193b11b46a2ce1a5bd38be6e892d3e5b Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Thu, 31 Oct 2024 17:54:04 +0100 Subject: [PATCH 01/28] Introduce `GeoLineStrings` archetype and support it in the map view --- .../re_types/definitions/rerun/archetypes.fbs | 1 + .../rerun/archetypes/geo_line_strings.fbs | 33 +++ .../re_types/definitions/rerun/components.fbs | 1 + .../rerun/components/geo_line_string.fbs | 15 + .../definitions/rerun/components/latlon.fbs | 2 +- .../re_types/src/archetypes/.gitattributes | 1 + .../src/archetypes/geo_line_strings.rs | 232 +++++++++++++++ crates/store/re_types/src/archetypes/mod.rs | 2 + .../re_types/src/components/.gitattributes | 1 + .../src/components/geo_line_string.rs | 265 ++++++++++++++++++ .../src/components/geo_line_string_ext.rs | 13 + .../store/re_types/src/components/lat_lon.rs | 2 +- crates/store/re_types/src/components/mod.rs | 3 + .../re_space_view_map/src/map_space_view.rs | 18 +- .../src/visualizers/geo_line_strings.rs | 189 +++++++++++++ .../re_space_view_map/src/visualizers/mod.rs | 21 +- crates/viewer/re_viewer/src/reflection/mod.rs | 29 +- docs/content/reference/types/archetypes.md | 1 + .../reference/types/archetypes/.gitattributes | 1 + .../types/archetypes/geo_line_strings.md | 24 ++ docs/content/reference/types/components.md | 3 +- .../reference/types/components/.gitattributes | 1 + .../reference/types/components/color.md | 1 + .../types/components/geo_line_string.md | 20 ++ .../reference/types/components/lat_lon.md | 2 +- .../reference/types/components/radius.md | 1 + .../reference/types/datatypes/dvec2d.md | 1 + .../content/reference/types/views/map_view.md | 1 + rerun_cpp/src/rerun/archetypes.hpp | 1 + rerun_cpp/src/rerun/archetypes/.gitattributes | 2 + .../src/rerun/archetypes/geo_line_strings.cpp | 43 +++ .../src/rerun/archetypes/geo_line_strings.hpp | 78 ++++++ rerun_cpp/src/rerun/components.hpp | 1 + rerun_cpp/src/rerun/components/.gitattributes | 2 + .../src/rerun/components/geo_line_string.cpp | 72 +++++ .../src/rerun/components/geo_line_string.hpp | 61 ++++ rerun_cpp/src/rerun/components/lat_lon.hpp | 2 +- rerun_py/rerun_sdk/rerun/__init__.py | 2 +- .../rerun_sdk/rerun/archetypes/.gitattributes | 1 + .../rerun_sdk/rerun/archetypes/__init__.py | 2 + .../rerun/archetypes/geo_line_strings.py | 104 +++++++ .../rerun_sdk/rerun/components/.gitattributes | 1 + .../rerun_sdk/rerun/components/__init__.py | 12 + .../rerun/components/geo_line_string.py | 77 +++++ .../rerun/components/geo_line_string_ext.py | 88 ++++++ .../rerun_sdk/rerun/components/lat_lon.py | 2 +- 46 files changed, 1423 insertions(+), 12 deletions(-) create mode 100644 crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs create mode 100644 crates/store/re_types/definitions/rerun/components/geo_line_string.fbs create mode 100644 crates/store/re_types/src/archetypes/geo_line_strings.rs create mode 100644 crates/store/re_types/src/components/geo_line_string.rs create mode 100644 crates/store/re_types/src/components/geo_line_string_ext.rs create mode 100644 crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs create mode 100644 docs/content/reference/types/archetypes/geo_line_strings.md create mode 100644 docs/content/reference/types/components/geo_line_string.md create mode 100644 rerun_cpp/src/rerun/archetypes/geo_line_strings.cpp create mode 100644 rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp create mode 100644 rerun_cpp/src/rerun/components/geo_line_string.cpp create mode 100644 rerun_cpp/src/rerun/components/geo_line_string.hpp create mode 100644 rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py create mode 100644 rerun_py/rerun_sdk/rerun/components/geo_line_string.py create mode 100644 rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py diff --git a/crates/store/re_types/definitions/rerun/archetypes.fbs b/crates/store/re_types/definitions/rerun/archetypes.fbs index 94db882d0ed9..0dfb4f20c146 100644 --- a/crates/store/re_types/definitions/rerun/archetypes.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes.fbs @@ -14,6 +14,7 @@ include "./archetypes/depth_image.fbs"; include "./archetypes/disconnected_space.fbs"; include "./archetypes/ellipsoids3d.fbs"; include "./archetypes/encoded_image.fbs"; +include "./archetypes/geo_line_strings.fbs"; include "./archetypes/geo_points.fbs"; include "./archetypes/image.fbs"; include "./archetypes/instance_poses3d.fbs"; diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs new file mode 100644 index 000000000000..436190963695 --- /dev/null +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs @@ -0,0 +1,33 @@ +namespace rerun.archetypes; + +// --- + +/// Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +/// +/// **Note**: Geospatial entities are experimental. +// TODO(ab): add snippet and screenshot +table GeoLineStrings ( + "attr.rust.derive": "PartialEq", + "attr.docs.category": "Geospatial", + "attr.docs.view_types": "MapView", + "attr.docs.unreleased" +) { + // --- Required --- + + /// The lines strings, expressed in EPSG:4326 coordinates. + line_strings: [rerun.components.GeoLineString] ("attr.rerun.component_required", order: 1000); + + // --- Recommended --- + + /// Optional radii for the line strings. + radii: [rerun.components.Radius] ("attr.rerun.component_recommended", nullable, order: 2000); + + /// Optional colors for the linestrings. + /// + /// \py The colors are interpreted as RGB or RGBA in sRGB gamma-space, + /// \py As either 0-1 floats or 0-255 integers, with separate alpha. + colors: [rerun.components.Color] ("attr.rerun.component_recommended", nullable, order: 2100); + + //TODO(ab): add `Label` and `ShowLabels` components + //TODO(ab): add `Altitude` component +} diff --git a/crates/store/re_types/definitions/rerun/components.fbs b/crates/store/re_types/definitions/rerun/components.fbs index e71676b88e2c..b1a32fd2133f 100644 --- a/crates/store/re_types/definitions/rerun/components.fbs +++ b/crates/store/re_types/definitions/rerun/components.fbs @@ -16,6 +16,7 @@ include "./components/entity_path.fbs"; include "./components/fill_mode.fbs"; include "./components/fill_ratio.fbs"; include "./components/gamma_correction.fbs"; +include "./components/geo_line_string.fbs"; include "./components/half_size2d.fbs"; include "./components/half_size3d.fbs"; include "./components/image_buffer.fbs"; diff --git a/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs b/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs new file mode 100644 index 000000000000..df905448337e --- /dev/null +++ b/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs @@ -0,0 +1,15 @@ + +namespace rerun.components; + +// --- + +/// A geospatial line string expressed in EPSG:4326 latitude and longitude. +table GeoLineString ( + "attr.python.aliases": "datatypes.DVec2DArrayLike, npt.NDArray[np.float64]", + "attr.python.array_aliases": "npt.NDArray[np.float64]", + "attr.rust.derive": "Default, PartialEq", + "attr.rust.repr": "transparent", + "attr.docs.unreleased" +) { + lat_lon: [rerun.datatypes.DVec2D] (order: 100); +} diff --git a/crates/store/re_types/definitions/rerun/components/latlon.fbs b/crates/store/re_types/definitions/rerun/components/latlon.fbs index 0508ad9140df..9a7d01dfccae 100644 --- a/crates/store/re_types/definitions/rerun/components/latlon.fbs +++ b/crates/store/re_types/definitions/rerun/components/latlon.fbs @@ -3,7 +3,7 @@ namespace rerun.components; // --- -/// A geographical position expressed in EPSG:4326 latitude and longitude. +/// A geospatial position expressed in EPSG:4326 latitude and longitude. struct LatLon ( "attr.python.aliases": "npt.NDArray[np.float32], Sequence[float], Tuple[float, float]", "attr.python.array_aliases": "npt.NDArray[np.float32], Sequence[float]", diff --git a/crates/store/re_types/src/archetypes/.gitattributes b/crates/store/re_types/src/archetypes/.gitattributes index 96c9be5cae2a..8496794357eb 100644 --- a/crates/store/re_types/src/archetypes/.gitattributes +++ b/crates/store/re_types/src/archetypes/.gitattributes @@ -14,6 +14,7 @@ depth_image.rs linguist-generated=true disconnected_space.rs linguist-generated=true ellipsoids3d.rs linguist-generated=true encoded_image.rs linguist-generated=true +geo_line_strings.rs linguist-generated=true geo_points.rs linguist-generated=true image.rs linguist-generated=true instance_poses3d.rs linguist-generated=true diff --git a/crates/store/re_types/src/archetypes/geo_line_strings.rs b/crates/store/re_types/src/archetypes/geo_line_strings.rs new file mode 100644 index 000000000000..63d21a470f51 --- /dev/null +++ b/crates/store/re_types/src/archetypes/geo_line_strings.rs @@ -0,0 +1,232 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs +// Based on "crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs". + +#![allow(unused_imports)] +#![allow(unused_parens)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::cloned_instead_of_copied)] +#![allow(clippy::map_flatten)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] + +use ::re_types_core::external::arrow2; +use ::re_types_core::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +/// +/// **Note**: Geospatial entities are experimental. +#[derive(Clone, Debug, PartialEq)] +pub struct GeoLineStrings { + /// The lines strings, expressed in EPSG:4326 coordinates. + pub line_strings: Vec, + + /// Optional radii for the line strings. + pub radii: Option>, + + /// Optional colors for the linestrings. + pub colors: Option>, +} + +impl ::re_types_core::SizeBytes for GeoLineStrings { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.line_strings.heap_size_bytes() + + self.radii.heap_size_bytes() + + self.colors.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + >::is_pod() + && >>::is_pod() + && >>::is_pod() + } +} + +static REQUIRED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 1usize]> = + once_cell::sync::Lazy::new(|| ["rerun.components.GeoLineString".into()]); + +static RECOMMENDED_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 3usize]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.Radius".into(), + "rerun.components.Color".into(), + "rerun.components.GeoLineStringsIndicator".into(), + ] + }); + +static OPTIONAL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 0usize]> = + once_cell::sync::Lazy::new(|| []); + +static ALL_COMPONENTS: once_cell::sync::Lazy<[ComponentName; 4usize]> = + once_cell::sync::Lazy::new(|| { + [ + "rerun.components.GeoLineString".into(), + "rerun.components.Radius".into(), + "rerun.components.Color".into(), + "rerun.components.GeoLineStringsIndicator".into(), + ] + }); + +impl GeoLineStrings { + /// The total number of components in the archetype: 1 required, 3 recommended, 0 optional + pub const NUM_COMPONENTS: usize = 4usize; +} + +/// Indicator component for the [`GeoLineStrings`] [`::re_types_core::Archetype`] +pub type GeoLineStringsIndicator = ::re_types_core::GenericIndicatorComponent; + +impl ::re_types_core::Archetype for GeoLineStrings { + type Indicator = GeoLineStringsIndicator; + + #[inline] + fn name() -> ::re_types_core::ArchetypeName { + "rerun.archetypes.GeoLineStrings".into() + } + + #[inline] + fn display_name() -> &'static str { + "Geo line strings" + } + + #[inline] + fn indicator() -> MaybeOwnedComponentBatch<'static> { + static INDICATOR: GeoLineStringsIndicator = GeoLineStringsIndicator::DEFAULT; + MaybeOwnedComponentBatch::Ref(&INDICATOR) + } + + #[inline] + fn required_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + REQUIRED_COMPONENTS.as_slice().into() + } + + #[inline] + fn recommended_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + RECOMMENDED_COMPONENTS.as_slice().into() + } + + #[inline] + fn optional_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + OPTIONAL_COMPONENTS.as_slice().into() + } + + #[inline] + fn all_components() -> ::std::borrow::Cow<'static, [ComponentName]> { + ALL_COMPONENTS.as_slice().into() + } + + #[inline] + fn from_arrow_components( + arrow_data: impl IntoIterator)>, + ) -> DeserializationResult { + re_tracing::profile_function!(); + use ::re_types_core::{Loggable as _, ResultExt as _}; + let arrays_by_name: ::std::collections::HashMap<_, _> = arrow_data + .into_iter() + .map(|(name, array)| (name.full_name(), array)) + .collect(); + let line_strings = { + let array = arrays_by_name + .get("rerun.components.GeoLineString") + .ok_or_else(DeserializationError::missing_data) + .with_context("rerun.archetypes.GeoLineStrings#line_strings")?; + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.GeoLineStrings#line_strings")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.GeoLineStrings#line_strings")? + }; + let radii = if let Some(array) = arrays_by_name.get("rerun.components.Radius") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.GeoLineStrings#radii")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.GeoLineStrings#radii")? + }) + } else { + None + }; + let colors = if let Some(array) = arrays_by_name.get("rerun.components.Color") { + Some({ + ::from_arrow_opt(&**array) + .with_context("rerun.archetypes.GeoLineStrings#colors")? + .into_iter() + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .collect::>>() + .with_context("rerun.archetypes.GeoLineStrings#colors")? + }) + } else { + None + }; + Ok(Self { + line_strings, + radii, + colors, + }) + } +} + +impl ::re_types_core::AsComponents for GeoLineStrings { + fn as_component_batches(&self) -> Vec> { + re_tracing::profile_function!(); + use ::re_types_core::Archetype as _; + [ + Some(Self::indicator()), + Some((&self.line_strings as &dyn ComponentBatch).into()), + self.radii + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + self.colors + .as_ref() + .map(|comp_batch| (comp_batch as &dyn ComponentBatch).into()), + ] + .into_iter() + .flatten() + .collect() + } +} + +impl ::re_types_core::ArchetypeReflectionMarker for GeoLineStrings {} + +impl GeoLineStrings { + /// Create a new `GeoLineStrings`. + #[inline] + pub fn new( + line_strings: impl IntoIterator>, + ) -> Self { + Self { + line_strings: line_strings.into_iter().map(Into::into).collect(), + radii: None, + colors: None, + } + } + + /// Optional radii for the line strings. + #[inline] + pub fn with_radii( + mut self, + radii: impl IntoIterator>, + ) -> Self { + self.radii = Some(radii.into_iter().map(Into::into).collect()); + self + } + + /// Optional colors for the linestrings. + #[inline] + pub fn with_colors( + mut self, + colors: impl IntoIterator>, + ) -> Self { + self.colors = Some(colors.into_iter().map(Into::into).collect()); + self + } +} diff --git a/crates/store/re_types/src/archetypes/mod.rs b/crates/store/re_types/src/archetypes/mod.rs index be1444e35445..03084a185d47 100644 --- a/crates/store/re_types/src/archetypes/mod.rs +++ b/crates/store/re_types/src/archetypes/mod.rs @@ -23,6 +23,7 @@ mod ellipsoids3d; mod ellipsoids3d_ext; mod encoded_image; mod encoded_image_ext; +mod geo_line_strings; mod geo_points; mod image; mod image_ext; @@ -65,6 +66,7 @@ pub use self::depth_image::DepthImage; pub use self::disconnected_space::DisconnectedSpace; pub use self::ellipsoids3d::Ellipsoids3D; pub use self::encoded_image::EncodedImage; +pub use self::geo_line_strings::GeoLineStrings; pub use self::geo_points::GeoPoints; pub use self::image::Image; pub use self::instance_poses3d::InstancePoses3D; diff --git a/crates/store/re_types/src/components/.gitattributes b/crates/store/re_types/src/components/.gitattributes index 1123598234cd..def154d84908 100644 --- a/crates/store/re_types/src/components/.gitattributes +++ b/crates/store/re_types/src/components/.gitattributes @@ -16,6 +16,7 @@ entity_path.rs linguist-generated=true fill_mode.rs linguist-generated=true fill_ratio.rs linguist-generated=true gamma_correction.rs linguist-generated=true +geo_line_string.rs linguist-generated=true half_size2d.rs linguist-generated=true half_size3d.rs linguist-generated=true image_buffer.rs linguist-generated=true diff --git a/crates/store/re_types/src/components/geo_line_string.rs b/crates/store/re_types/src/components/geo_line_string.rs new file mode 100644 index 000000000000..a62cb2790167 --- /dev/null +++ b/crates/store/re_types/src/components/geo_line_string.rs @@ -0,0 +1,265 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs +// Based on "crates/store/re_types/definitions/rerun/components/geo_line_string.fbs". + +#![allow(unused_imports)] +#![allow(unused_parens)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::cloned_instead_of_copied)] +#![allow(clippy::map_flatten)] +#![allow(clippy::needless_question_mark)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::too_many_lines)] + +use ::re_types_core::external::arrow2; +use ::re_types_core::ComponentName; +use ::re_types_core::SerializationResult; +use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; +use ::re_types_core::{DeserializationError, DeserializationResult}; + +/// **Component**: A geospatial line string expressed in EPSG:4326 latitude and longitude. +#[derive(Clone, Debug, Default, PartialEq)] +#[repr(transparent)] +pub struct GeoLineString(pub Vec); + +impl ::re_types_core::SizeBytes for GeoLineString { + #[inline] + fn heap_size_bytes(&self) -> u64 { + self.0.heap_size_bytes() + } + + #[inline] + fn is_pod() -> bool { + >::is_pod() + } +} + +impl, T: IntoIterator> From for GeoLineString { + fn from(v: T) -> Self { + Self(v.into_iter().map(|v| v.into()).collect()) + } +} + +::re_types_core::macros::impl_into_cow!(GeoLineString); + +impl ::re_types_core::Loggable for GeoLineString { + type Name = ::re_types_core::ComponentName; + + #[inline] + fn name() -> Self::Name { + "rerun.components.GeoLineString".into() + } + + #[inline] + fn arrow_datatype() -> arrow2::datatypes::DataType { + #![allow(clippy::wildcard_imports)] + use arrow2::datatypes::*; + DataType::List(std::sync::Arc::new(Field::new( + "item", + ::arrow_datatype(), + false, + ))) + } + + fn to_arrow_opt<'a>( + data: impl IntoIterator>>>, + ) -> SerializationResult> + where + Self: Clone + 'a, + { + #![allow(clippy::wildcard_imports)] + #![allow(clippy::manual_is_variant_and)] + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, datatypes::*}; + Ok({ + let (somes, data0): (Vec<_>, Vec<_>) = data + .into_iter() + .map(|datum| { + let datum: Option<::std::borrow::Cow<'a, Self>> = datum.map(Into::into); + let datum = datum.map(|datum| datum.into_owned().0); + (datum.is_some(), datum) + }) + .unzip(); + let data0_bitmap: Option = { + let any_nones = somes.iter().any(|some| !*some); + any_nones.then(|| somes.into()) + }; + { + use arrow2::{buffer::Buffer, offset::OffsetsBuffer}; + let offsets = arrow2::offset::Offsets::::try_from_lengths( + data0 + .iter() + .map(|opt| opt.as_ref().map_or(0, |datum| datum.len())), + )? + .into(); + let data0_inner_data: Vec<_> = data0.into_iter().flatten().flatten().collect(); + let data0_inner_bitmap: Option = None; + ListArray::try_new( + Self::arrow_datatype(), + offsets, + { + use arrow2::{buffer::Buffer, offset::OffsetsBuffer}; + let data0_inner_data_inner_data: Vec<_> = data0_inner_data + .into_iter() + .map(|datum| datum.0) + .flatten() + .collect(); + let data0_inner_data_inner_bitmap: Option = None; + FixedSizeListArray::new( + DataType::FixedSizeList( + std::sync::Arc::new(Field::new("item", DataType::Float64, false)), + 2usize, + ), + PrimitiveArray::new( + DataType::Float64, + data0_inner_data_inner_data.into_iter().collect(), + data0_inner_data_inner_bitmap, + ) + .boxed(), + data0_inner_bitmap, + ) + .boxed() + }, + data0_bitmap, + )? + .boxed() + } + }) + } + + fn from_arrow_opt( + arrow_data: &dyn arrow2::array::Array, + ) -> DeserializationResult>> + where + Self: Sized, + { + #![allow(clippy::wildcard_imports)] + use ::re_types_core::{Loggable as _, ResultExt as _}; + use arrow2::{array::*, buffer::*, datatypes::*}; + Ok({ + let arrow_data = arrow_data + .as_any() + .downcast_ref::>() + .ok_or_else(|| { + let expected = Self::arrow_datatype(); + let actual = arrow_data.data_type().clone(); + DeserializationError::datatype_mismatch(expected, actual) + }) + .with_context("rerun.components.GeoLineString#lat_lon")?; + if arrow_data.is_empty() { + Vec::new() + } else { + let arrow_data_inner = { + let arrow_data_inner = &**arrow_data.values(); + { + let arrow_data_inner = arrow_data_inner + .as_any() + .downcast_ref::() + .ok_or_else(|| { + let expected = DataType::FixedSizeList( + std::sync::Arc::new(Field::new( + "item", + DataType::Float64, + false, + )), + 2usize, + ); + let actual = arrow_data_inner.data_type().clone(); + DeserializationError::datatype_mismatch(expected, actual) + }) + .with_context("rerun.components.GeoLineString#lat_lon")?; + if arrow_data_inner.is_empty() { + Vec::new() + } else { + let offsets = (0..) + .step_by(2usize) + .zip((2usize..).step_by(2usize).take(arrow_data_inner.len())); + let arrow_data_inner_inner = { + let arrow_data_inner_inner = &**arrow_data_inner.values(); + arrow_data_inner_inner + .as_any() + .downcast_ref::() + .ok_or_else(|| { + let expected = DataType::Float64; + let actual = arrow_data_inner_inner.data_type().clone(); + DeserializationError::datatype_mismatch(expected, actual) + }) + .with_context("rerun.components.GeoLineString#lat_lon")? + .into_iter() + .map(|opt| opt.copied()) + .collect::>() + }; + arrow2::bitmap::utils::ZipValidity::new_with_validity( + offsets, + arrow_data_inner.validity(), + ) + .map(|elem| { + elem.map(|(start, end): (usize, usize)| { + debug_assert!(end - start == 2usize); + if end > arrow_data_inner_inner.len() { + return Err(DeserializationError::offset_slice_oob( + (start, end), + arrow_data_inner_inner.len(), + )); + } + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + let data = + unsafe { arrow_data_inner_inner.get_unchecked(start..end) }; + let data = data.iter().cloned().map(Option::unwrap_or_default); + + // NOTE: Unwrapping cannot fail: the length must be correct. + #[allow(clippy::unwrap_used)] + Ok(array_init::from_iter(data).unwrap()) + }) + .transpose() + }) + .map(|res_or_opt| { + res_or_opt + .map(|res_or_opt| res_or_opt.map(crate::datatypes::DVec2D)) + }) + .collect::>>>()? + } + .into_iter() + } + .collect::>() + }; + let offsets = arrow_data.offsets(); + arrow2::bitmap::utils::ZipValidity::new_with_validity( + offsets.iter().zip(offsets.lengths()), + arrow_data.validity(), + ) + .map(|elem| { + elem.map(|(start, len)| { + let start = *start as usize; + let end = start + len; + if end > arrow_data_inner.len() { + return Err(DeserializationError::offset_slice_oob( + (start, end), + arrow_data_inner.len(), + )); + } + + #[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + let data = unsafe { arrow_data_inner.get_unchecked(start..end) }; + let data = data + .iter() + .cloned() + .map(Option::unwrap_or_default) + .collect(); + Ok(data) + }) + .transpose() + }) + .collect::>>>()? + } + .into_iter() + } + .map(|v| v.ok_or_else(DeserializationError::missing_data)) + .map(|res| res.map(|v| Some(Self(v)))) + .collect::>>>() + .with_context("rerun.components.GeoLineString#lat_lon") + .with_context("rerun.components.GeoLineString")?) + } +} diff --git a/crates/store/re_types/src/components/geo_line_string_ext.rs b/crates/store/re_types/src/components/geo_line_string_ext.rs new file mode 100644 index 000000000000..542824f41b83 --- /dev/null +++ b/crates/store/re_types/src/components/geo_line_string_ext.rs @@ -0,0 +1,13 @@ +use crate::datatypes::DVec2D; + +use super::GeoLineString; + +// --- + +impl GeoLineString { + /// Create a new line string from a list of positions. + #[allow(clippy::should_implement_trait)] // vanilla `FromIter` is too limiting in what it can express + pub fn from_iter(points: impl IntoIterator>) -> Self { + Self(points.into_iter().map(Into::into).collect()) + } +} diff --git a/crates/store/re_types/src/components/lat_lon.rs b/crates/store/re_types/src/components/lat_lon.rs index eda8ada719f8..6a35e4e234fd 100644 --- a/crates/store/re_types/src/components/lat_lon.rs +++ b/crates/store/re_types/src/components/lat_lon.rs @@ -18,7 +18,7 @@ use ::re_types_core::SerializationResult; use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Component**: A geographical position expressed in EPSG:4326 latitude and longitude. +/// **Component**: A geospatial position expressed in EPSG:4326 latitude and longitude. #[derive(Clone, Debug, Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] #[repr(transparent)] pub struct LatLon(pub crate::datatypes::DVec2D); diff --git a/crates/store/re_types/src/components/mod.rs b/crates/store/re_types/src/components/mod.rs index 21d399a1142c..ee02251c6341 100644 --- a/crates/store/re_types/src/components/mod.rs +++ b/crates/store/re_types/src/components/mod.rs @@ -24,6 +24,8 @@ mod fill_ratio; mod fill_ratio_ext; mod gamma_correction; mod gamma_correction_ext; +mod geo_line_string; +mod geo_line_string_ext; mod half_size2d; mod half_size2d_ext; mod half_size3d; @@ -132,6 +134,7 @@ pub use self::entity_path::EntityPath; pub use self::fill_mode::FillMode; pub use self::fill_ratio::FillRatio; pub use self::gamma_correction::GammaCorrection; +pub use self::geo_line_string::GeoLineString; pub use self::half_size2d::HalfSize2D; pub use self::half_size3d::HalfSize3D; pub use self::image_buffer::ImageBuffer; diff --git a/crates/viewer/re_space_view_map/src/map_space_view.rs b/crates/viewer/re_space_view_map/src/map_space_view.rs index bf4e6ad580a2..19dd662ef737 100644 --- a/crates/viewer/re_space_view_map/src/map_space_view.rs +++ b/crates/viewer/re_space_view_map/src/map_space_view.rs @@ -24,7 +24,7 @@ use re_viewer_context::{ use re_viewport_blueprint::ViewProperty; use crate::map_overlays; -use crate::visualizers::geo_points::GeoPointsVisualizer; +use crate::visualizers::{update_span, GeoLineStringsVisualizer, GeoPointsVisualizer}; #[derive(Default)] pub struct MapSpaceViewState { @@ -103,7 +103,8 @@ Displays geospatial primitives on a map. &self, system_registry: &mut SpaceViewSystemRegistrator<'_>, ) -> Result<(), SpaceViewClassRegistryError> { - system_registry.register_visualizer::() + system_registry.register_visualizer::()?; + system_registry.register_visualizer::() } fn new_state(&self) -> Box { @@ -161,6 +162,9 @@ Displays geospatial primitives on a map. ); let geo_points_visualizer = system_output.view_systems.get::()?; + let geo_line_strings_visualizers = system_output + .view_systems + .get::()?; // // Map Provider @@ -188,7 +192,9 @@ Displays geospatial primitives on a map. // changes in walkers // TODO(#7884): support more elaborate auto-pan/zoom modes. - let span = geo_points_visualizer.span(); + let mut span = None; + update_span(&mut span, geo_points_visualizer.span()); + update_span(&mut span, geo_line_strings_visualizers.span()); let default_center_position = span .as_ref() @@ -262,6 +268,12 @@ Displays geospatial primitives on a map. let mut view_builder = create_view_builder(render_ctx, ui.ctx(), map_rect, &query.highlights); + geo_line_strings_visualizers.queue_draw_data( + render_ctx, + &mut view_builder, + &projector, + &query.highlights, + )?; geo_points_visualizer.queue_draw_data( render_ctx, &mut view_builder, diff --git a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs new file mode 100644 index 000000000000..2b8e1da0eb96 --- /dev/null +++ b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs @@ -0,0 +1,189 @@ +use re_log_types::{EntityPath, Instance}; +use re_renderer::{renderer::LineDrawDataError, PickingLayerInstanceId}; +use re_space_view::{DataResultQuery as _, RangeResultsExt as _}; +use re_types::{ + archetypes::GeoLineStrings, + components::{Color, GeoLineString, Radius}, + Loggable as _, +}; +use re_viewer_context::{ + auto_color_for_entity_path, IdentifiedViewSystem, QueryContext, SpaceViewHighlights, + SpaceViewSystemExecutionError, TypedComponentFallbackProvider, ViewContext, + ViewContextCollection, ViewQuery, VisualizerQueryInfo, VisualizerSystem, +}; + +#[derive(Debug, Default)] +struct GeoLineStringsBatch { + lines: Vec>, + //TODO(#7872): to be converted to scene vs. ui + radii: Vec, + colors: Vec, + instance_id: Vec, +} + +/// Visualizer for [`GeoPoints`]. +#[derive(Default)] +pub struct GeoLineStringsVisualizer { + batches: Vec<(EntityPath, GeoLineStringsBatch)>, +} + +impl IdentifiedViewSystem for GeoLineStringsVisualizer { + fn identifier() -> re_viewer_context::ViewSystemIdentifier { + "GeoLineStrings".into() + } +} + +impl VisualizerSystem for GeoLineStringsVisualizer { + fn visualizer_query_info(&self) -> VisualizerQueryInfo { + VisualizerQueryInfo::from_archetype::() + } + + fn execute( + &mut self, + ctx: &ViewContext<'_>, + view_query: &ViewQuery<'_>, + _context_systems: &ViewContextCollection, + ) -> Result, SpaceViewSystemExecutionError> { + for data_result in view_query.iter_visible_data_results(ctx, Self::identifier()) { + let results = + data_result.query_archetype_with_history::(ctx, view_query); + + let mut batch_data = GeoLineStringsBatch::default(); + + // gather all relevant chunks + let timeline = view_query.timeline; + let all_lines = results.iter_as(timeline, GeoLineString::name()); + let all_colors = results.iter_as(timeline, Color::name()); + let all_radii = results.iter_as(timeline, Radius::name()); + + // fallback component values + let fallback_color: Color = + self.fallback_for(&ctx.query_context(data_result, &view_query.latest_at_query())); + let fallback_radius: Radius = + self.fallback_for(&ctx.query_context(data_result, &view_query.latest_at_query())); + + // iterate over each chunk and find all relevant component slices + for (_index, lines, colors, radii) in re_query::range_zip_1x2( + all_lines.component::(), + all_colors.component::(), + all_radii.component::(), + ) { + // required component + let lines = lines.as_slice(); + + // optional components + let colors = colors.as_ref().map(|c| c.as_slice()).unwrap_or(&[]); + let radii = radii.as_ref().map(|r| r.as_slice()).unwrap_or(&[]); + + // optional components values to be used for instance clamping semantics + let last_color = colors.last().copied().unwrap_or(fallback_color); + let last_radii = radii.last().copied().unwrap_or(fallback_radius); + + // iterate over all instances + for (instance_index, (line, color, radius)) in itertools::izip!( + lines, + colors.iter().chain(std::iter::repeat(&last_color)), + radii.iter().chain(std::iter::repeat(&last_radii)), + ) + .enumerate() + { + batch_data.lines.push( + line.0 + .iter() + .map(|pos| walkers::Position::from_lat_lon(pos.x(), pos.y())) + .collect(), + ); + batch_data.radii.push(radius.0.abs()); + batch_data.colors.push(color.0.into()); + batch_data + .instance_id + .push(re_renderer::PickingLayerInstanceId(instance_index as _)); + } + } + + self.batches + .push((data_result.entity_path.clone(), batch_data)); + } + + Ok(Vec::new()) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn fallback_provider(&self) -> &dyn re_viewer_context::ComponentFallbackProvider { + self + } +} + +impl GeoLineStringsVisualizer { + /// Compute the [`super::GeoSpan`] of all the points in the visualizer. + pub fn span(&self) -> Option { + super::GeoSpan::from_lat_long( + self.batches + .iter() + .flat_map(|(_, batch)| batch.lines.iter()) + .flatten() + .map(|pos| (pos.lat(), pos.lon())), + ) + } + + pub fn queue_draw_data( + &self, + render_ctx: &re_renderer::RenderContext, + view_builder: &mut re_renderer::ViewBuilder, + projector: &walkers::Projector, + highlight: &SpaceViewHighlights, + ) -> Result<(), LineDrawDataError> { + let mut lines = re_renderer::LineDrawableBuilder::new(render_ctx); + + for (entity_path, batch) in &self.batches { + let outline = highlight.entity_outline_mask(entity_path.hash()); + + let mut line_batch = lines + .batch(entity_path.to_string()) + .picking_object_id(re_renderer::PickingLayerObjectId(entity_path.hash64())) + .outline_mask_ids(outline.overall); + + let entity_highlight = highlight.entity_outline_mask(entity_path.hash()); + + for (strip, radius, color, instance) in itertools::izip!( + &batch.lines, + &batch.radii, + &batch.colors, + &batch.instance_id + ) { + line_batch + .add_strip_2d(strip.iter().map(|pos| { + let ui_position = projector.project(*pos); + glam::vec2(ui_position.x, ui_position.y) + })) + .radius(re_renderer::Size(*radius)) + .color(*color) + .picking_instance_id(*instance) + .outline_mask_ids( + entity_highlight.index_outline_mask(Instance::from(instance.0)), + ); + } + } + + view_builder.queue_draw(lines.into_draw_data()?); + + Ok(()) + } +} + +impl TypedComponentFallbackProvider for GeoLineStringsVisualizer { + fn fallback_for(&self, ctx: &QueryContext<'_>) -> Color { + auto_color_for_entity_path(ctx.target_entity_path) + } +} + +impl TypedComponentFallbackProvider for GeoLineStringsVisualizer { + fn fallback_for(&self, _ctx: &QueryContext<'_>) -> Radius { + Radius::from(5.0) + } +} + +re_viewer_context::impl_component_fallback_provider!(GeoLineStringsVisualizer => [Color, Radius]); diff --git a/crates/viewer/re_space_view_map/src/visualizers/mod.rs b/crates/viewer/re_space_view_map/src/visualizers/mod.rs index dd0b986eb60c..189d27224730 100644 --- a/crates/viewer/re_space_view_map/src/visualizers/mod.rs +++ b/crates/viewer/re_space_view_map/src/visualizers/mod.rs @@ -1,4 +1,8 @@ -pub mod geo_points; +mod geo_line_strings; +mod geo_points; + +pub use geo_line_strings::GeoLineStringsVisualizer; +pub use geo_points::GeoPointsVisualizer; /// Helper to track an area span in latitude and longitude. #[derive(Debug, Clone)] @@ -73,6 +77,21 @@ impl GeoSpan { } } +/// Extend a span to include another span, if any. +pub fn update_span(span: &mut Option, other: Option) { + if let Some(other) = other { + match span { + Some(span) => { + span.min_latitude = span.min_latitude.min(other.min_latitude); + span.max_latitude = span.max_latitude.max(other.max_latitude); + span.min_longitude = span.min_longitude.min(other.min_longitude); + span.max_longitude = span.max_longitude.max(other.max_longitude); + } + None => *span = Some(other), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index 573ba11dd7e5..6184fd88e090 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -438,6 +438,14 @@ fn generate_component_reflection() -> Result::name(), + ComponentReflection { + docstring_md: "A geospatial line string expressed in EPSG:4326 latitude and longitude.", + custom_placeholder: Some(GeoLineString::default().to_arrow()?), + datatype: GeoLineString::arrow_datatype(), + }, + ), ( ::name(), ComponentReflection { @@ -489,7 +497,7 @@ fn generate_component_reflection() -> Result::name(), ComponentReflection { - docstring_md: "A geographical position expressed in EPSG:4326 latitude and longitude.", + docstring_md: "A geospatial position expressed in EPSG:4326 latitude and longitude.", custom_placeholder: Some(LatLon::default().to_arrow()?), datatype: LatLon::arrow_datatype(), }, @@ -1236,6 +1244,25 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { ], }, ), + ( + ArchetypeName::new("rerun.archetypes.GeoLineStrings"), + ArchetypeReflection { + display_name: "Geo line strings", + fields: vec![ + ArchetypeFieldReflection { component_name : + "rerun.components.GeoLineString".into(), display_name : + "Line strings", docstring_md : + "The lines strings, expressed in EPSG:4326 coordinates.", is_required + : true, }, ArchetypeFieldReflection { component_name : + "rerun.components.Radius".into(), display_name : "Radii", + docstring_md : "Optional radii for the line strings.", is_required : + false, }, ArchetypeFieldReflection { component_name : + "rerun.components.Color".into(), display_name : "Colors", + docstring_md : "Optional colors for the linestrings.", is_required : + false, }, + ], + }, + ), ( ArchetypeName::new("rerun.archetypes.GeoPoints"), ArchetypeReflection { diff --git a/docs/content/reference/types/archetypes.md b/docs/content/reference/types/archetypes.md index 5430bb87fb13..8a9c26a05f65 100644 --- a/docs/content/reference/types/archetypes.md +++ b/docs/content/reference/types/archetypes.md @@ -14,6 +14,7 @@ This page lists all built-in archetypes. ## Geospatial +* [`GeoLineStrings`](archetypes/geo_line_strings.md): Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. * [`GeoPoints`](archetypes/geo_points.md): Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. ## Image & tensor diff --git a/docs/content/reference/types/archetypes/.gitattributes b/docs/content/reference/types/archetypes/.gitattributes index be19a5c3dcd2..65b162a42607 100644 --- a/docs/content/reference/types/archetypes/.gitattributes +++ b/docs/content/reference/types/archetypes/.gitattributes @@ -15,6 +15,7 @@ depth_image.md linguist-generated=true disconnected_space.md linguist-generated=true ellipsoids3d.md linguist-generated=true encoded_image.md linguist-generated=true +geo_line_strings.md linguist-generated=true geo_points.md linguist-generated=true image.md linguist-generated=true instance_poses3d.md linguist-generated=true diff --git a/docs/content/reference/types/archetypes/geo_line_strings.md b/docs/content/reference/types/archetypes/geo_line_strings.md new file mode 100644 index 000000000000..15a847339958 --- /dev/null +++ b/docs/content/reference/types/archetypes/geo_line_strings.md @@ -0,0 +1,24 @@ +--- +title: "GeoLineStrings" +--- + + +Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. + +**Note**: Geospatial entities are experimental. + +## Components + +**Required**: [`GeoLineString`](../components/geo_line_string.md) + +**Recommended**: [`Radius`](../components/radius.md), [`Color`](../components/color.md) + +## Shown in +* [MapView](../views/map_view.md) +* [DataframeView](../views/dataframe_view.md) + +## API reference links + * 🌊 [C++ API docs for `GeoLineStrings`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1archetypes_1_1GeoLineStrings.html?speculative-link) + * 🐍 [Python API docs for `GeoLineStrings`](https://ref.rerun.io/docs/python/stable/common/archetypes?speculative-link#rerun.archetypes.GeoLineStrings) + * 🦀 [Rust API docs for `GeoLineStrings`](https://docs.rs/rerun/latest/rerun/archetypes/struct.GeoLineStrings.html?speculative-link) + diff --git a/docs/content/reference/types/components.md b/docs/content/reference/types/components.md index ef8e567b987f..8412f5cd7f45 100644 --- a/docs/content/reference/types/components.md +++ b/docs/content/reference/types/components.md @@ -29,13 +29,14 @@ on [Entities and Components](../../concepts/entity-component.md). * [`FillMode`](components/fill_mode.md): How a geometric shape is drawn and colored. * [`FillRatio`](components/fill_ratio.md): How much a primitive fills out the available space. * [`GammaCorrection`](components/gamma_correction.md): A gamma correction value to be used with a scalar value or color. +* [`GeoLineString`](components/geo_line_string.md): A geospatial line string expressed in EPSG:4326 latitude and longitude. * [`HalfSize2D`](components/half_size2d.md): Half-size (radius) of a 2D box. * [`HalfSize3D`](components/half_size3d.md): Half-size (radius) of a 3D box. * [`ImageBuffer`](components/image_buffer.md): A buffer that is known to store image data. * [`ImageFormat`](components/image_format.md): The metadata describing the contents of a [`components.ImageBuffer`](https://rerun.io/docs/reference/types/components/image_buffer). * [`ImagePlaneDistance`](components/image_plane_distance.md): The distance from the camera origin to the image plane when the projection is shown in a 3D viewer. * [`KeypointId`](components/keypoint_id.md): A 16-bit ID representing a type of semantic keypoint within a class. -* [`LatLon`](components/lat_lon.md): A geographical position expressed in EPSG:4326 latitude and longitude. +* [`LatLon`](components/lat_lon.md): A geospatial position expressed in EPSG:4326 latitude and longitude. * [`Length`](components/length.md): Length, or one-dimensional size. * [`LineStrip2D`](components/line_strip2d.md): A line strip in 2D space. * [`LineStrip3D`](components/line_strip3d.md): A line strip in 3D space. diff --git a/docs/content/reference/types/components/.gitattributes b/docs/content/reference/types/components/.gitattributes index e9324b406b03..d2b7740214d4 100644 --- a/docs/content/reference/types/components/.gitattributes +++ b/docs/content/reference/types/components/.gitattributes @@ -17,6 +17,7 @@ entity_path.md linguist-generated=true fill_mode.md linguist-generated=true fill_ratio.md linguist-generated=true gamma_correction.md linguist-generated=true +geo_line_string.md linguist-generated=true half_size2d.md linguist-generated=true half_size3d.md linguist-generated=true image_buffer.md linguist-generated=true diff --git a/docs/content/reference/types/components/color.md b/docs/content/reference/types/components/color.md index f5bc0b564424..9569cc8603f2 100644 --- a/docs/content/reference/types/components/color.md +++ b/docs/content/reference/types/components/color.md @@ -32,6 +32,7 @@ uint32 * [`Boxes3D`](../archetypes/boxes3d.md) * [`Capsules3D`](../archetypes/capsules3d.md?speculative-link) * [`Ellipsoids3D`](../archetypes/ellipsoids3d.md) +* [`GeoLineStrings`](../archetypes/geo_line_strings.md?speculative-link) * [`GeoPoints`](../archetypes/geo_points.md?speculative-link) * [`LineStrips2D`](../archetypes/line_strips2d.md) * [`LineStrips3D`](../archetypes/line_strips3d.md) diff --git a/docs/content/reference/types/components/geo_line_string.md b/docs/content/reference/types/components/geo_line_string.md new file mode 100644 index 000000000000..e2657ebca7aa --- /dev/null +++ b/docs/content/reference/types/components/geo_line_string.md @@ -0,0 +1,20 @@ +--- +title: "GeoLineString" +--- + + +A geospatial line string expressed in EPSG:4326 latitude and longitude. + +## Fields + +* lat_lon: list of [`DVec2D`](../datatypes/dvec2d.md) + +## API reference links + * 🌊 [C++ API docs for `GeoLineString`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1GeoLineString.html?speculative-link) + * 🐍 [Python API docs for `GeoLineString`](https://ref.rerun.io/docs/python/stable/common/components?speculative-link#rerun.components.GeoLineString) + * 🦀 [Rust API docs for `GeoLineString`](https://docs.rs/rerun/latest/rerun/components/struct.GeoLineString.html?speculative-link) + + +## Used by + +* [`GeoLineStrings`](../archetypes/geo_line_strings.md?speculative-link) diff --git a/docs/content/reference/types/components/lat_lon.md b/docs/content/reference/types/components/lat_lon.md index 149eb16262e7..a11ad797e89c 100644 --- a/docs/content/reference/types/components/lat_lon.md +++ b/docs/content/reference/types/components/lat_lon.md @@ -3,7 +3,7 @@ title: "LatLon" --- -A geographical position expressed in EPSG:4326 latitude and longitude. +A geospatial position expressed in EPSG:4326 latitude and longitude. ## Rerun datatype [`DVec2D`](../datatypes/dvec2d.md) diff --git a/docs/content/reference/types/components/radius.md b/docs/content/reference/types/components/radius.md index 96f175d26fc9..4d13afca0265 100644 --- a/docs/content/reference/types/components/radius.md +++ b/docs/content/reference/types/components/radius.md @@ -35,6 +35,7 @@ float32 * [`Boxes3D`](../archetypes/boxes3d.md) * [`Capsules3D`](../archetypes/capsules3d.md?speculative-link) * [`Ellipsoids3D`](../archetypes/ellipsoids3d.md) +* [`GeoLineStrings`](../archetypes/geo_line_strings.md?speculative-link) * [`GeoPoints`](../archetypes/geo_points.md?speculative-link) * [`LineStrips2D`](../archetypes/line_strips2d.md) * [`LineStrips3D`](../archetypes/line_strips3d.md) diff --git a/docs/content/reference/types/datatypes/dvec2d.md b/docs/content/reference/types/datatypes/dvec2d.md index ab2e8f604707..ce176d390cbc 100644 --- a/docs/content/reference/types/datatypes/dvec2d.md +++ b/docs/content/reference/types/datatypes/dvec2d.md @@ -19,4 +19,5 @@ FixedSizeList<2, float64> ## Used by +* [`GeoLineString`](../components/geo_line_string.md?speculative-link) * [`LatLon`](../components/lat_lon.md?speculative-link) diff --git a/docs/content/reference/types/views/map_view.md b/docs/content/reference/types/views/map_view.md index 730d94cbd175..9960e35f45eb 100644 --- a/docs/content/reference/types/views/map_view.md +++ b/docs/content/reference/types/views/map_view.md @@ -24,5 +24,6 @@ snippet: views/map ## Visualized archetypes +* [`GeoLineStrings`](../archetypes/geo_line_strings.md) * [`GeoPoints`](../archetypes/geo_points.md) diff --git a/rerun_cpp/src/rerun/archetypes.hpp b/rerun_cpp/src/rerun/archetypes.hpp index 158cc4004553..2bb1e89eaac5 100644 --- a/rerun_cpp/src/rerun/archetypes.hpp +++ b/rerun_cpp/src/rerun/archetypes.hpp @@ -16,6 +16,7 @@ #include "archetypes/disconnected_space.hpp" #include "archetypes/ellipsoids3d.hpp" #include "archetypes/encoded_image.hpp" +#include "archetypes/geo_line_strings.hpp" #include "archetypes/geo_points.hpp" #include "archetypes/image.hpp" #include "archetypes/instance_poses3d.hpp" diff --git a/rerun_cpp/src/rerun/archetypes/.gitattributes b/rerun_cpp/src/rerun/archetypes/.gitattributes index 602f53bd013d..979a5d48e652 100644 --- a/rerun_cpp/src/rerun/archetypes/.gitattributes +++ b/rerun_cpp/src/rerun/archetypes/.gitattributes @@ -29,6 +29,8 @@ ellipsoids3d.cpp linguist-generated=true ellipsoids3d.hpp linguist-generated=true encoded_image.cpp linguist-generated=true encoded_image.hpp linguist-generated=true +geo_line_strings.cpp linguist-generated=true +geo_line_strings.hpp linguist-generated=true geo_points.cpp linguist-generated=true geo_points.hpp linguist-generated=true image.cpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/archetypes/geo_line_strings.cpp b/rerun_cpp/src/rerun/archetypes/geo_line_strings.cpp new file mode 100644 index 000000000000..5ceeeea4eae4 --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/geo_line_strings.cpp @@ -0,0 +1,43 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs". + +#include "geo_line_strings.hpp" + +#include "../collection_adapter_builtins.hpp" + +namespace rerun::archetypes {} + +namespace rerun { + + Result> AsComponents::serialize( + const archetypes::GeoLineStrings& archetype + ) { + using namespace archetypes; + std::vector cells; + cells.reserve(4); + + { + auto result = ComponentBatch::from_loggable(archetype.line_strings); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.radii.has_value()) { + auto result = ComponentBatch::from_loggable(archetype.radii.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + if (archetype.colors.has_value()) { + auto result = ComponentBatch::from_loggable(archetype.colors.value()); + RR_RETURN_NOT_OK(result.error); + cells.push_back(std::move(result.value)); + } + { + auto indicator = GeoLineStrings::IndicatorComponent(); + auto result = ComponentBatch::from_loggable(indicator); + RR_RETURN_NOT_OK(result.error); + cells.emplace_back(std::move(result.value)); + } + + return cells; + } +} // namespace rerun diff --git a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp new file mode 100644 index 000000000000..fb4a56ad2070 --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp @@ -0,0 +1,78 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs". + +#pragma once + +#include "../collection.hpp" +#include "../compiler_utils.hpp" +#include "../component_batch.hpp" +#include "../components/color.hpp" +#include "../components/geo_line_string.hpp" +#include "../components/radius.hpp" +#include "../indicator_component.hpp" +#include "../result.hpp" + +#include +#include +#include +#include + +namespace rerun::archetypes { + /// **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. + /// + /// **Note**: Geospatial entities are experimental. + struct GeoLineStrings { + /// The lines strings, expressed in EPSG:4326 coordinates. + Collection line_strings; + + /// Optional radii for the line strings. + std::optional> radii; + + /// Optional colors for the linestrings. + std::optional> colors; + + public: + static constexpr const char IndicatorComponentName[] = + "rerun.components.GeoLineStringsIndicator"; + + /// Indicator component, used to identify the archetype when converting to a list of components. + using IndicatorComponent = rerun::components::IndicatorComponent; + + public: + GeoLineStrings() = default; + GeoLineStrings(GeoLineStrings&& other) = default; + + explicit GeoLineStrings(Collection _line_strings) + : line_strings(std::move(_line_strings)) {} + + /// Optional radii for the line strings. + GeoLineStrings with_radii(Collection _radii) && { + radii = std::move(_radii); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + + /// Optional colors for the linestrings. + GeoLineStrings with_colors(Collection _colors) && { + colors = std::move(_colors); + // See: https://github.com/rerun-io/rerun/issues/4027 + RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) + } + }; + +} // namespace rerun::archetypes + +namespace rerun { + /// \private + template + struct AsComponents; + + /// \private + template <> + struct AsComponents { + /// Serialize all set component batches. + static Result> serialize( + const archetypes::GeoLineStrings& archetype + ); + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components.hpp b/rerun_cpp/src/rerun/components.hpp index 61c44812fa65..8806bc1c90f5 100644 --- a/rerun_cpp/src/rerun/components.hpp +++ b/rerun_cpp/src/rerun/components.hpp @@ -18,6 +18,7 @@ #include "components/fill_mode.hpp" #include "components/fill_ratio.hpp" #include "components/gamma_correction.hpp" +#include "components/geo_line_string.hpp" #include "components/half_size2d.hpp" #include "components/half_size3d.hpp" #include "components/image_buffer.hpp" diff --git a/rerun_cpp/src/rerun/components/.gitattributes b/rerun_cpp/src/rerun/components/.gitattributes index fe754806b3d7..7fb37fd6dd9b 100644 --- a/rerun_cpp/src/rerun/components/.gitattributes +++ b/rerun_cpp/src/rerun/components/.gitattributes @@ -21,6 +21,8 @@ fill_mode.cpp linguist-generated=true fill_mode.hpp linguist-generated=true fill_ratio.hpp linguist-generated=true gamma_correction.hpp linguist-generated=true +geo_line_string.cpp linguist-generated=true +geo_line_string.hpp linguist-generated=true half_size2d.hpp linguist-generated=true half_size3d.hpp linguist-generated=true image_buffer.hpp linguist-generated=true diff --git a/rerun_cpp/src/rerun/components/geo_line_string.cpp b/rerun_cpp/src/rerun/components/geo_line_string.cpp new file mode 100644 index 000000000000..05cf6c8bf846 --- /dev/null +++ b/rerun_cpp/src/rerun/components/geo_line_string.cpp @@ -0,0 +1,72 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/store/re_types/definitions/rerun/components/geo_line_string.fbs". + +#include "geo_line_string.hpp" + +#include "../datatypes/dvec2d.hpp" + +#include +#include + +namespace rerun::components {} + +namespace rerun { + const std::shared_ptr& Loggable::arrow_datatype() { + static const auto datatype = arrow::list( + arrow::field("item", Loggable::arrow_datatype(), false) + ); + return datatype; + } + + Result> Loggable::to_arrow( + const components::GeoLineString* instances, size_t num_instances + ) { + // TODO(andreas): Allow configuring the memory pool. + arrow::MemoryPool* pool = arrow::default_memory_pool(); + auto datatype = arrow_datatype(); + + ARROW_ASSIGN_OR_RAISE(auto builder, arrow::MakeBuilder(datatype, pool)) + if (instances && num_instances > 0) { + RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( + static_cast(builder.get()), + instances, + num_instances + )); + } + std::shared_ptr array; + ARROW_RETURN_NOT_OK(builder->Finish(&array)); + return array; + } + + rerun::Error Loggable::fill_arrow_array_builder( + arrow::ListBuilder* builder, const components::GeoLineString* elements, size_t num_elements + ) { + if (builder == nullptr) { + return rerun::Error(ErrorCode::UnexpectedNullArgument, "Passed array builder is null."); + } + if (elements == nullptr) { + return rerun::Error( + ErrorCode::UnexpectedNullArgument, + "Cannot serialize null pointer to arrow array." + ); + } + + auto value_builder = static_cast(builder->value_builder()); + ARROW_RETURN_NOT_OK(builder->Reserve(static_cast(num_elements))); + ARROW_RETURN_NOT_OK(value_builder->Reserve(static_cast(num_elements * 2))); + + for (size_t elem_idx = 0; elem_idx < num_elements; elem_idx += 1) { + const auto& element = elements[elem_idx]; + ARROW_RETURN_NOT_OK(builder->Append()); + if (element.lat_lon.data()) { + RR_RETURN_NOT_OK(Loggable::fill_arrow_array_builder( + value_builder, + element.lat_lon.data(), + element.lat_lon.size() + )); + } + } + + return Error::ok(); + } +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/geo_line_string.hpp b/rerun_cpp/src/rerun/components/geo_line_string.hpp new file mode 100644 index 000000000000..ed1bbfeff415 --- /dev/null +++ b/rerun_cpp/src/rerun/components/geo_line_string.hpp @@ -0,0 +1,61 @@ +// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/cpp/mod.rs +// Based on "crates/store/re_types/definitions/rerun/components/geo_line_string.fbs". + +#pragma once + +#include "../collection.hpp" +#include "../datatypes/dvec2d.hpp" +#include "../result.hpp" + +#include +#include +#include + +namespace arrow { + class Array; + class DataType; + class ListBuilder; +} // namespace arrow + +namespace rerun::components { + /// **Component**: A geospatial line string expressed in EPSG:4326 latitude and longitude. + struct GeoLineString { + rerun::Collection lat_lon; + + public: + GeoLineString() = default; + + GeoLineString(rerun::Collection lat_lon_) + : lat_lon(std::move(lat_lon_)) {} + + GeoLineString& operator=(rerun::Collection lat_lon_) { + lat_lon = std::move(lat_lon_); + return *this; + } + }; +} // namespace rerun::components + +namespace rerun { + template + struct Loggable; + + /// \private + template <> + struct Loggable { + static constexpr const char Name[] = "rerun.components.GeoLineString"; + + /// Returns the arrow data type this type corresponds to. + static const std::shared_ptr& arrow_datatype(); + + /// Serializes an array of `rerun::components::GeoLineString` into an arrow array. + static Result> to_arrow( + const components::GeoLineString* instances, size_t num_instances + ); + + /// Fills an arrow array builder with an array of this type. + static rerun::Error fill_arrow_array_builder( + arrow::ListBuilder* builder, const components::GeoLineString* elements, + size_t num_elements + ); + }; +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/lat_lon.hpp b/rerun_cpp/src/rerun/components/lat_lon.hpp index c17a4be43c97..3189887b4ed0 100644 --- a/rerun_cpp/src/rerun/components/lat_lon.hpp +++ b/rerun_cpp/src/rerun/components/lat_lon.hpp @@ -11,7 +11,7 @@ #include namespace rerun::components { - /// **Component**: A geographical position expressed in EPSG:4326 latitude and longitude. + /// **Component**: A geospatial position expressed in EPSG:4326 latitude and longitude. struct LatLon { rerun::datatypes::DVec2D lat_lon; diff --git a/rerun_py/rerun_sdk/rerun/__init__.py b/rerun_py/rerun_sdk/rerun/__init__.py index c513e77079c2..302faf075a70 100644 --- a/rerun_py/rerun_sdk/rerun/__init__.py +++ b/rerun_py/rerun_sdk/rerun/__init__.py @@ -12,7 +12,6 @@ __version__ = "0.20.0-alpha.1+dev" __version_info__ = (0, 20, 0, "alpha.1") - if sys.version_info < (3, 9): warnings.warn( "Python 3.8 is past EOL (https://devguide.python.org/versions/). Rerun version 0.21 will drop support/testing of Python 3.8.", @@ -76,6 +75,7 @@ Ellipsoids3D as Ellipsoids3D, EncodedImage as EncodedImage, GeoPoints as GeoPoints, + GeoLineStrings as GeoLineStrings, Image as Image, InstancePoses3D as InstancePoses3D, LineStrips2D as LineStrips2D, diff --git a/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes b/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes index 20aeae8ac993..e7a8ba7d6a2f 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/archetypes/.gitattributes @@ -16,6 +16,7 @@ depth_image.py linguist-generated=true disconnected_space.py linguist-generated=true ellipsoids3d.py linguist-generated=true encoded_image.py linguist-generated=true +geo_line_strings.py linguist-generated=true geo_points.py linguist-generated=true image.py linguist-generated=true instance_poses3d.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/archetypes/__init__.py b/rerun_py/rerun_sdk/rerun/archetypes/__init__.py index ad2810683969..0324104dc218 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/__init__.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/__init__.py @@ -16,6 +16,7 @@ from .disconnected_space import DisconnectedSpace from .ellipsoids3d import Ellipsoids3D from .encoded_image import EncodedImage +from .geo_line_strings import GeoLineStrings from .geo_points import GeoPoints from .image import Image from .instance_poses3d import InstancePoses3D @@ -51,6 +52,7 @@ "DisconnectedSpace", "Ellipsoids3D", "EncodedImage", + "GeoLineStrings", "GeoPoints", "Image", "InstancePoses3D", diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py new file mode 100644 index 000000000000..2ac891d13396 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py @@ -0,0 +1,104 @@ +# DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/python/mod.rs +# Based on "crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs". + +# You can extend this class by creating a "GeoLineStringsExt" class in "geo_line_strings_ext.py". + +from __future__ import annotations + +from typing import Any + +from attrs import define, field + +from .. import components, datatypes +from .._baseclasses import ( + Archetype, +) +from ..error_utils import catch_and_log_exceptions + +__all__ = ["GeoLineStrings"] + + +@define(str=False, repr=False, init=False) +class GeoLineStrings(Archetype): + """ + **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. + + **Note**: Geospatial entities are experimental. + """ + + def __init__( + self: Any, + line_strings: components.GeoLineStringArrayLike, + *, + radii: datatypes.Float32ArrayLike | None = None, + colors: datatypes.Rgba32ArrayLike | None = None, + ): + """ + Create a new instance of the GeoLineStrings archetype. + + Parameters + ---------- + line_strings: + The lines strings, expressed in EPSG:4326 coordinates. + radii: + Optional radii for the line strings. + colors: + Optional colors for the linestrings. + + The colors are interpreted as RGB or RGBA in sRGB gamma-space, + As either 0-1 floats or 0-255 integers, with separate alpha. + + """ + + # You can define your own __init__ function as a member of GeoLineStringsExt in geo_line_strings_ext.py + with catch_and_log_exceptions(context=self.__class__.__name__): + self.__attrs_init__(line_strings=line_strings, radii=radii, colors=colors) + return + self.__attrs_clear__() + + def __attrs_clear__(self) -> None: + """Convenience method for calling `__attrs_init__` with all `None`s.""" + self.__attrs_init__( + line_strings=None, # type: ignore[arg-type] + radii=None, # type: ignore[arg-type] + colors=None, # type: ignore[arg-type] + ) + + @classmethod + def _clear(cls) -> GeoLineStrings: + """Produce an empty GeoLineStrings, bypassing `__init__`.""" + inst = cls.__new__(cls) + inst.__attrs_clear__() + return inst + + line_strings: components.GeoLineStringBatch = field( + metadata={"component": "required"}, + converter=components.GeoLineStringBatch._required, # type: ignore[misc] + ) + # The lines strings, expressed in EPSG:4326 coordinates. + # + # (Docstring intentionally commented out to hide this field from the docs) + + radii: components.RadiusBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.RadiusBatch._optional, # type: ignore[misc] + ) + # Optional radii for the line strings. + # + # (Docstring intentionally commented out to hide this field from the docs) + + colors: components.ColorBatch | None = field( + metadata={"component": "optional"}, + default=None, + converter=components.ColorBatch._optional, # type: ignore[misc] + ) + # Optional colors for the linestrings. + # + # The colors are interpreted as RGB or RGBA in sRGB gamma-space, + # As either 0-1 floats or 0-255 integers, with separate alpha. + # + # (Docstring intentionally commented out to hide this field from the docs) + + __str__ = Archetype.__str__ + __repr__ = Archetype.__repr__ # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/components/.gitattributes b/rerun_py/rerun_sdk/rerun/components/.gitattributes index b9ec2f413529..b27be864833b 100644 --- a/rerun_py/rerun_sdk/rerun/components/.gitattributes +++ b/rerun_py/rerun_sdk/rerun/components/.gitattributes @@ -18,6 +18,7 @@ entity_path.py linguist-generated=true fill_mode.py linguist-generated=true fill_ratio.py linguist-generated=true gamma_correction.py linguist-generated=true +geo_line_string.py linguist-generated=true half_size2d.py linguist-generated=true half_size3d.py linguist-generated=true image_buffer.py linguist-generated=true diff --git a/rerun_py/rerun_sdk/rerun/components/__init__.py b/rerun_py/rerun_sdk/rerun/components/__init__.py index 89339d1ce9e6..a756fd4e3071 100644 --- a/rerun_py/rerun_sdk/rerun/components/__init__.py +++ b/rerun_py/rerun_sdk/rerun/components/__init__.py @@ -30,6 +30,13 @@ from .fill_mode import FillMode, FillModeArrayLike, FillModeBatch, FillModeLike, FillModeType from .fill_ratio import FillRatio, FillRatioBatch, FillRatioType from .gamma_correction import GammaCorrection, GammaCorrectionBatch, GammaCorrectionType +from .geo_line_string import ( + GeoLineString, + GeoLineStringArrayLike, + GeoLineStringBatch, + GeoLineStringLike, + GeoLineStringType, +) from .half_size2d import HalfSize2D, HalfSize2DBatch, HalfSize2DType from .half_size3d import HalfSize3D, HalfSize3DBatch, HalfSize3DType from .image_buffer import ImageBuffer, ImageBufferBatch, ImageBufferType @@ -153,6 +160,11 @@ "GammaCorrection", "GammaCorrectionBatch", "GammaCorrectionType", + "GeoLineString", + "GeoLineStringArrayLike", + "GeoLineStringBatch", + "GeoLineStringLike", + "GeoLineStringType", "HalfSize2D", "HalfSize2DBatch", "HalfSize2DType", diff --git a/rerun_py/rerun_sdk/rerun/components/geo_line_string.py b/rerun_py/rerun_sdk/rerun/components/geo_line_string.py new file mode 100644 index 000000000000..e51999590027 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/geo_line_string.py @@ -0,0 +1,77 @@ +# DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/python/mod.rs +# Based on "crates/store/re_types/definitions/rerun/components/geo_line_string.fbs". + +# You can extend this class by creating a "GeoLineStringExt" class in "geo_line_string_ext.py". + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Sequence, Union + +import numpy as np +import numpy.typing as npt +import pyarrow as pa +from attrs import define, field + +from .. import datatypes +from .._baseclasses import ( + BaseBatch, + BaseExtensionType, + ComponentBatchMixin, + ComponentMixin, +) +from .geo_line_string_ext import GeoLineStringExt + +__all__ = ["GeoLineString", "GeoLineStringArrayLike", "GeoLineStringBatch", "GeoLineStringLike", "GeoLineStringType"] + + +@define(init=False) +class GeoLineString(GeoLineStringExt, ComponentMixin): + """**Component**: A geospatial line string expressed in EPSG:4326 latitude and longitude.""" + + _BATCH_TYPE = None + + def __init__(self: Any, lat_lon: GeoLineStringLike): + """Create a new instance of the GeoLineString component.""" + + # You can define your own __init__ function as a member of GeoLineStringExt in geo_line_string_ext.py + self.__attrs_init__(lat_lon=lat_lon) + + lat_lon: list[datatypes.DVec2D] = field() + + +if TYPE_CHECKING: + GeoLineStringLike = Union[GeoLineString, datatypes.DVec2DArrayLike, npt.NDArray[np.float64]] +else: + GeoLineStringLike = Any + +GeoLineStringArrayLike = Union[GeoLineString, Sequence[GeoLineStringLike], npt.NDArray[np.float64]] + + +class GeoLineStringType(BaseExtensionType): + _TYPE_NAME: str = "rerun.components.GeoLineString" + + def __init__(self) -> None: + pa.ExtensionType.__init__( + self, + pa.list_( + pa.field( + "item", + pa.list_(pa.field("item", pa.float64(), nullable=False, metadata={}), 2), + nullable=False, + metadata={}, + ) + ), + self._TYPE_NAME, + ) + + +class GeoLineStringBatch(BaseBatch[GeoLineStringArrayLike], ComponentBatchMixin): + _ARROW_TYPE = GeoLineStringType() + + @staticmethod + def _native_to_pa_array(data: GeoLineStringArrayLike, data_type: pa.DataType) -> pa.Array: + return GeoLineStringExt.native_to_pa_array_override(data, data_type) + + +# This is patched in late to avoid circular dependencies. +GeoLineString._BATCH_TYPE = GeoLineStringBatch # type: ignore[assignment] diff --git a/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py b/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py new file mode 100644 index 000000000000..75b77dff280d --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +import numbers +from collections.abc import Sized +from typing import TYPE_CHECKING, Any, Sequence + +import numpy as np +import pyarrow as pa + +if TYPE_CHECKING: + from . import GeoLineStringArrayLike + + +def next_offset(acc: int, arr: Sized) -> int: + return acc + len(arr) + + +class GeoLineStringExt: + """Extension for [GeoLineString][rerun.components.GeoLineString].""" + + @staticmethod + def native_to_pa_array_override(data: GeoLineStringArrayLike, data_type: pa.DataType) -> pa.Array: + from ..datatypes import DVec2DBatch + from . import GeoLineString + + # pure-numpy fast path + if isinstance(data, np.ndarray): + if len(data) == 0: + inners = [] + elif data.ndim == 2: + inners = [DVec2DBatch(data).as_arrow_array().storage] + else: + o = 0 + offsets = [o] + [o := next_offset(o, arr) for arr in data] + inner = DVec2DBatch(data.reshape(-1)).as_arrow_array().storage + return pa.ListArray.from_arrays(offsets, inner, type=data_type) + + # pure-object + elif isinstance(data, GeoLineString): + inners = [DVec2DBatch(data.points).as_arrow_array().storage] + + # sequences + elif isinstance(data, Sequence): + if len(data) == 0: + inners = [] + else: + # Is it a single strip or several? + # It could be a sequence of the style `[[0, 0], [1, 1]]` which is a single strip. + if isinstance(data[0], Sequence) and len(data[0]) > 0 and isinstance(data[0][0], numbers.Number): + if len(data[0]) == 2: # type: ignore[arg-type] + # If any of the following elements are not sequence of length 2, DVec2DBatch should raise an error. + inners = [DVec2DBatch(data).as_arrow_array().storage] # type: ignore[arg-type] + else: + raise ValueError( + "Expected a sequence of sequences of 2D vectors, but the inner sequence length was not equal to 2." + ) + # It could be a sequence of the style `[np.array([0, 0]), np.array([1, 1])]` which is a single strip. + elif isinstance(data[0], np.ndarray) and data[0].shape == (2,): + # If any of the following elements are not np arrays of shape 2, DVec2DBatch should raise an error. + inners = [DVec2DBatch(data).as_arrow_array().storage] # type: ignore[arg-type] + # .. otherwise assume that it's several strips. + else: + + def to_DVec2D_batch(strip: Any) -> DVec2DBatch: + if isinstance(strip, GeoLineString): + return DVec2DBatch(strip.points) + else: + if isinstance(strip, np.ndarray) and (strip.ndim != 2 or strip.shape[1] != 2): + raise ValueError( + "Expected a sequence of 2D vectors, instead got array with shape {strip.shape}." + ) + return DVec2DBatch(strip) + + inners = [to_DVec2D_batch(strip).as_arrow_array().storage for strip in data] + else: + inners = [DVec2DBatch(data).storage] + + if len(inners) == 0: + offsets = pa.array([0], type=pa.int32()) + inner = DVec2DBatch([]).as_arrow_array().storage + return pa.ListArray.from_arrays(offsets, inner, type=data_type) + + o = 0 + offsets = [o] + [o := next_offset(o, inner) for inner in inners] + + inner = pa.concat_arrays(inners) + + return pa.ListArray.from_arrays(offsets, inner, type=data_type) diff --git a/rerun_py/rerun_sdk/rerun/components/lat_lon.py b/rerun_py/rerun_sdk/rerun/components/lat_lon.py index 2777c2737dea..68f70620033d 100644 --- a/rerun_py/rerun_sdk/rerun/components/lat_lon.py +++ b/rerun_py/rerun_sdk/rerun/components/lat_lon.py @@ -15,7 +15,7 @@ class LatLon(datatypes.DVec2D, ComponentMixin): - """**Component**: A geographical position expressed in EPSG:4326 latitude and longitude.""" + """**Component**: A geospatial position expressed in EPSG:4326 latitude and longitude.""" _BATCH_TYPE = None # You can define your own __init__ function as a member of LatLonExt in lat_lon_ext.py From b507744019052dce4136a20d49a234b5637a0df5 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 11:55:43 +0100 Subject: [PATCH 02/28] Add snippet and screenshot for the `GeoPoints` archetype --- .../rerun/archetypes/geo_points.fbs | 6 ++-- .../re_types/src/archetypes/geo_points.rs | 27 ++++++++++++++++++ .../reference/types/archetypes/geo_points.md | 14 ++++++++++ .../all/archetypes/geo_point_simple.cpp | 15 ++++++++++ .../all/archetypes/geo_point_simple.py | 14 ++++++++++ .../all/archetypes/geo_point_simple.rs | 14 ++++++++++ rerun_cpp/src/rerun/archetypes/geo_points.hpp | 21 ++++++++++++++ .../rerun_sdk/rerun/archetypes/geo_points.py | 28 +++++++++++++++++++ 8 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 docs/snippets/all/archetypes/geo_point_simple.cpp create mode 100644 docs/snippets/all/archetypes/geo_point_simple.py create mode 100644 docs/snippets/all/archetypes/geo_point_simple.rs diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs index 9402342ceaac..413dca5d490a 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs @@ -1,11 +1,11 @@ namespace rerun.archetypes; -// --- - /// Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. -// TODO(ab): add snippet and screenshot +/// +/// \example archetypes/geo_point_simple title="Log a geospatial point" image="https://static.rerun.io/geopoint_simple/146b0783c5aea1c1b6a9aab938879942b7c820e2/1200w.png" +// TODO(ab): screenshot table GeoPoints ( "attr.rust.derive": "PartialEq", "attr.docs.category": "Geospatial", diff --git a/crates/store/re_types/src/archetypes/geo_points.rs b/crates/store/re_types/src/archetypes/geo_points.rs index 621ae23bdb09..da7ddb0e5aff 100644 --- a/crates/store/re_types/src/archetypes/geo_points.rs +++ b/crates/store/re_types/src/archetypes/geo_points.rs @@ -21,6 +21,33 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// **Archetype**: Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. +/// +/// ## Example +/// +/// ### Log a geospatial point +/// ```ignore +/// fn main() -> Result<(), Box> { +/// let rec = rerun::RecordingStreamBuilder::new("rerun_example_geo_points").spawn()?; +/// +/// rec.log( +/// "rerun_hq", +/// &rerun::GeoPoints::new([(59.319221, 18.075631)]) +/// .with_radii([rerun::Radius::new_ui_points(10.0)]) +/// .with_colors([rerun::Color::from_rgb(255, 0, 0)]), +/// )?; +/// +/// Ok(()) +/// } +/// ``` +///
+/// +/// +/// +/// +/// +/// +/// +///
#[derive(Clone, Debug, PartialEq)] pub struct GeoPoints { /// The EPSG:4326 coordinates for the points. diff --git a/docs/content/reference/types/archetypes/geo_points.md b/docs/content/reference/types/archetypes/geo_points.md index c0816b3199cd..0b01a9a7d352 100644 --- a/docs/content/reference/types/archetypes/geo_points.md +++ b/docs/content/reference/types/archetypes/geo_points.md @@ -22,3 +22,17 @@ Geospatial points with positions expressed in EPSG:4326 altitude and longitude, * 🐍 [Python API docs for `GeoPoints`](https://ref.rerun.io/docs/python/stable/common/archetypes?speculative-link#rerun.archetypes.GeoPoints) * 🦀 [Rust API docs for `GeoPoints`](https://docs.rs/rerun/latest/rerun/archetypes/struct.GeoPoints.html?speculative-link) +## Example + +### Log a geospatial point + +snippet: archetypes/geo_point_simple + + + + + + + + + diff --git a/docs/snippets/all/archetypes/geo_point_simple.cpp b/docs/snippets/all/archetypes/geo_point_simple.cpp new file mode 100644 index 000000000000..ed326c5fcdec --- /dev/null +++ b/docs/snippets/all/archetypes/geo_point_simple.cpp @@ -0,0 +1,15 @@ +// Log some very simple geospatial point. + +#include + +int main() { + const auto rec = rerun::RecordingStream("rerun_example_geo_points"); + rec.spawn().exit_on_failure(); + + rec.log( + "rerun_hq", + rerun::GeoPoints({{59.319221, 18.075631}}) + .with_radii(rerun::Radius::ui_points(10.0f)) + .with_colors(rerun::Color(255, 0, 0)) + ); +} diff --git a/docs/snippets/all/archetypes/geo_point_simple.py b/docs/snippets/all/archetypes/geo_point_simple.py new file mode 100644 index 000000000000..f30e7db8944c --- /dev/null +++ b/docs/snippets/all/archetypes/geo_point_simple.py @@ -0,0 +1,14 @@ +"""Log some very simple geospatial point.""" + +import rerun as rr + +rr.init("rerun_example_geo_points", spawn=True) + +rr.log( + "rerun_hq", + rr.GeoPoints( + [59.319221, 18.075631], + radii=rr.Radius.ui_points(10.0), + colors=[255, 0, 0], + ), +) diff --git a/docs/snippets/all/archetypes/geo_point_simple.rs b/docs/snippets/all/archetypes/geo_point_simple.rs new file mode 100644 index 000000000000..60df477a6a41 --- /dev/null +++ b/docs/snippets/all/archetypes/geo_point_simple.rs @@ -0,0 +1,14 @@ +//! Log some very simple geospatial point. + +fn main() -> Result<(), Box> { + let rec = rerun::RecordingStreamBuilder::new("rerun_example_geo_points").spawn()?; + + rec.log( + "rerun_hq", + &rerun::GeoPoints::new([(59.319221, 18.075631)]) + .with_radii([rerun::Radius::new_ui_points(10.0)]) + .with_colors([rerun::Color::from_rgb(255, 0, 0)]), + )?; + + Ok(()) +} diff --git a/rerun_cpp/src/rerun/archetypes/geo_points.hpp b/rerun_cpp/src/rerun/archetypes/geo_points.hpp index 4f58ca761083..35ddefde2960 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_points.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_points.hpp @@ -21,6 +21,27 @@ namespace rerun::archetypes { /// **Archetype**: Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. + /// + /// ## Example + /// + /// ### Log a geospatial point + /// ![image](https://static.rerun.io/geopoint_simple/146b0783c5aea1c1b6a9aab938879942b7c820e2/full.png) + /// + /// ```cpp + /// #include + /// + /// int main() { + /// const auto rec = rerun::RecordingStream("rerun_example_geo_points"); + /// rec.spawn().exit_on_failure(); + /// + /// rec.log( + /// "rerun_hq", + /// rerun::GeoPoints({{59.319221, 18.075631}}) + /// .with_radii(rerun::Radius::ui_points(10.0f)) + /// .with_colors(rerun::Color(255, 0, 0)) + /// ); + /// } + /// ``` struct GeoPoints { /// The EPSG:4326 coordinates for the points. Collection positions; diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py index 3cec4449b52b..e83054f56dca 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py @@ -24,6 +24,34 @@ class GeoPoints(Archetype): **Archetype**: Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. **Note**: Geospatial entities are experimental. + + Example + ------- + ### Log a geospatial point: + ```python + import rerun as rr + + rr.init("rerun_example_geo_points", spawn=True) + + rr.log( + "rerun_hq", + rr.GeoPoints( + [59.319221, 18.075631], + radii=rr.Radius.ui_points(10.0), + colors=[255, 0, 0], + ), + ) + ``` +
+ + + + + + + +
+ """ def __init__( From 034c32784d4e26aaa9aecb6318171fa804377f43 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 12:23:35 +0100 Subject: [PATCH 03/28] Fix view heuristics --- .../re_space_view_map/src/map_space_view.rs | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/crates/viewer/re_space_view_map/src/map_space_view.rs b/crates/viewer/re_space_view_map/src/map_space_view.rs index 19dd662ef737..55cce1032b5e 100644 --- a/crates/viewer/re_space_view_map/src/map_space_view.rs +++ b/crates/viewer/re_space_view_map/src/map_space_view.rs @@ -5,7 +5,6 @@ use re_data_ui::{item_ui, DataUi}; use re_entity_db::InstancePathHash; use re_log_types::EntityPath; use re_renderer::{RenderContext, ViewBuilder}; -use re_space_view::suggest_space_view_for_each_entity; use re_types::{ blueprint::{ archetypes::{MapBackground, MapZoom}, @@ -16,10 +15,10 @@ use re_types::{ }; use re_ui::list_item; use re_viewer_context::{ - gpu_bridge, Item, SpaceViewClass, SpaceViewClassLayoutPriority, SpaceViewClassRegistryError, - SpaceViewHighlights, SpaceViewId, SpaceViewSpawnHeuristics, SpaceViewState, - SpaceViewStateExt as _, SpaceViewSystemExecutionError, SpaceViewSystemRegistrator, - SystemExecutionOutput, UiLayout, ViewQuery, ViewerContext, + gpu_bridge, IdentifiedViewSystem as _, Item, SpaceViewClass, SpaceViewClassLayoutPriority, + SpaceViewClassRegistryError, SpaceViewHighlights, SpaceViewId, SpaceViewSpawnHeuristics, + SpaceViewState, SpaceViewStateExt as _, SpaceViewSystemExecutionError, + SpaceViewSystemRegistrator, SystemExecutionOutput, UiLayout, ViewQuery, ViewerContext, }; use re_viewport_blueprint::ViewProperty; @@ -117,11 +116,29 @@ Displays geospatial primitives on a map. } fn layout_priority(&self) -> SpaceViewClassLayoutPriority { - SpaceViewClassLayoutPriority::Low + SpaceViewClassLayoutPriority::default() } fn spawn_heuristics(&self, ctx: &ViewerContext<'_>) -> SpaceViewSpawnHeuristics { - suggest_space_view_for_each_entity::(ctx, self) + re_tracing::profile_function!(); + + // Spawn a single map view at the root if any geospatial entity exists. + let any_map_entity = [ + GeoPointsVisualizer::identifier(), + GeoLineStringsVisualizer::identifier(), + ] + .iter() + .any(|system_id| { + ctx.indicated_entities_per_visualizer + .get(system_id) + .is_some_and(|indicated_entities| !indicated_entities.is_empty()) + }); + + if any_map_entity { + SpaceViewSpawnHeuristics::root() + } else { + SpaceViewSpawnHeuristics::default() + } } fn selection_ui( From f078708b6cc3f0989846f11d72275c17c51d34ab Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 12:38:14 +0100 Subject: [PATCH 04/28] Add `LatLon` and `DVec2D` to `rerun.hpp` --- rerun_cpp/src/rerun.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rerun_cpp/src/rerun.hpp b/rerun_cpp/src/rerun.hpp index e15d3ce115d1..a01db0f7765c 100644 --- a/rerun_cpp/src/rerun.hpp +++ b/rerun_cpp/src/rerun.hpp @@ -35,6 +35,7 @@ namespace rerun { using components::FillMode; using components::HalfSize2D; using components::HalfSize3D; + using components::LatLon; using components::LineStrip2D; using components::LineStrip3D; using components::MediaType; @@ -53,6 +54,7 @@ namespace rerun { using datatypes::ChannelDatatype; using datatypes::ClassDescription; using datatypes::ColorModel; + using datatypes::DVec2D; using datatypes::Float32; using datatypes::KeypointPair; using datatypes::Mat3x3; From 970e5881ddd86f6836dc73e9df281f7a05163fad Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 16:15:25 +0100 Subject: [PATCH 05/28] Add snippet (but not screenshot!) for `GeoLineStrings` --- .../rerun/archetypes/geo_line_strings.fbs | 4 ++- .../src/archetypes/geo_line_strings.rs | 24 +++++++++++++++++ .../types/archetypes/geo_line_strings.md | 6 +++++ .../all/archetypes/geo_line_string_simple.cpp | 22 ++++++++++++++++ .../all/archetypes/geo_line_string_simple.py | 20 ++++++++++++++ .../all/archetypes/geo_line_string_simple.rs | 20 ++++++++++++++ .../src/rerun/archetypes/geo_line_strings.hpp | 26 +++++++++++++++++++ .../rerun/archetypes/geo_line_strings.py | 25 ++++++++++++++++++ 8 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 docs/snippets/all/archetypes/geo_line_string_simple.cpp create mode 100644 docs/snippets/all/archetypes/geo_line_string_simple.py create mode 100644 docs/snippets/all/archetypes/geo_line_string_simple.rs diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs index 436190963695..db5ef8ce7afb 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs @@ -5,7 +5,9 @@ namespace rerun.archetypes; /// Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. -// TODO(ab): add snippet and screenshot +/// +/// \example archetypes/geo_line_string_simple title="Log a geospatial line string" +// TODO(ab): add screenshot table GeoLineStrings ( "attr.rust.derive": "PartialEq", "attr.docs.category": "Geospatial", diff --git a/crates/store/re_types/src/archetypes/geo_line_strings.rs b/crates/store/re_types/src/archetypes/geo_line_strings.rs index 63d21a470f51..eba6f051679f 100644 --- a/crates/store/re_types/src/archetypes/geo_line_strings.rs +++ b/crates/store/re_types/src/archetypes/geo_line_strings.rs @@ -21,6 +21,30 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. +/// +/// ## Example +/// +/// ### Log a geospatial line string +/// ```ignore +/// fn main() -> Result<(), Box> { +/// let rec = rerun::RecordingStreamBuilder::new("rerun_example_geo_line_strings").spawn()?; +/// +/// rec.log( +/// "colorado", +/// &rerun::GeoLineStrings::new([[ +/// [41.0000, -109.0452], +/// [41.0000, -102.0415], +/// [36.9931, -102.0415], +/// [36.9931, -109.0452], +/// [41.0000, -109.0452], +/// ]]) +/// .with_radii([rerun::Radius::new_ui_points(2.0)]) +/// .with_colors([rerun::Color::from_rgb(0, 0, 255)]), +/// )?; +/// +/// Ok(()) +/// } +/// ``` #[derive(Clone, Debug, PartialEq)] pub struct GeoLineStrings { /// The lines strings, expressed in EPSG:4326 coordinates. diff --git a/docs/content/reference/types/archetypes/geo_line_strings.md b/docs/content/reference/types/archetypes/geo_line_strings.md index 15a847339958..4b87dca1fc85 100644 --- a/docs/content/reference/types/archetypes/geo_line_strings.md +++ b/docs/content/reference/types/archetypes/geo_line_strings.md @@ -22,3 +22,9 @@ Geospatial line strings with positions expressed in EPSG:4326 altitude and longi * 🐍 [Python API docs for `GeoLineStrings`](https://ref.rerun.io/docs/python/stable/common/archetypes?speculative-link#rerun.archetypes.GeoLineStrings) * 🦀 [Rust API docs for `GeoLineStrings`](https://docs.rs/rerun/latest/rerun/archetypes/struct.GeoLineStrings.html?speculative-link) +## Example + +### Log a geospatial line string + +snippet: archetypes/geo_line_string_simple + diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.cpp b/docs/snippets/all/archetypes/geo_line_string_simple.cpp new file mode 100644 index 000000000000..ab3adc2c3168 --- /dev/null +++ b/docs/snippets/all/archetypes/geo_line_string_simple.cpp @@ -0,0 +1,22 @@ +// Log a simple geospatial line string. + +#include + +int main() { + const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); + rec.spawn().exit_on_failure(); + + rerun::Collection line_string = { + {41.0000, -109.0452}, + {41.0000, -102.0415}, + {36.9931, -102.0415}, + {36.9931, -109.0452}, + {41.0000, -109.0452}}; + + rec.log( + "colorado", + rerun::GeoLineStrings({line_string}) + .with_radii(rerun::Radius::ui_points(2.0f)) + .with_colors(rerun::Color(0, 0, 255)) + ); +} diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.py b/docs/snippets/all/archetypes/geo_line_string_simple.py new file mode 100644 index 000000000000..2485fcfd518f --- /dev/null +++ b/docs/snippets/all/archetypes/geo_line_string_simple.py @@ -0,0 +1,20 @@ +"""Log a simple geospatial line string.""" + +import rerun as rr + +rr.init("rerun_example_geo_line_strings", spawn=True) + +rr.log( + "colorado", + rr.GeoLineStrings( + [ + [41.0000, -109.0452], + [41.0000, -102.0415], + [36.9931, -102.0415], + [36.9931, -109.0452], + [41.0000, -109.0452], + ], + radii=rr.Radius.ui_points(2.0), + colors=[0, 0, 255], + ), +) diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.rs b/docs/snippets/all/archetypes/geo_line_string_simple.rs new file mode 100644 index 000000000000..f9fb9654b576 --- /dev/null +++ b/docs/snippets/all/archetypes/geo_line_string_simple.rs @@ -0,0 +1,20 @@ +//! Log a simple geospatial line string. + +fn main() -> Result<(), Box> { + let rec = rerun::RecordingStreamBuilder::new("rerun_example_geo_line_strings").spawn()?; + + rec.log( + "colorado", + &rerun::GeoLineStrings::new([[ + [41.0000, -109.0452], + [41.0000, -102.0415], + [36.9931, -102.0415], + [36.9931, -109.0452], + [41.0000, -109.0452], + ]]) + .with_radii([rerun::Radius::new_ui_points(2.0)]) + .with_colors([rerun::Color::from_rgb(0, 0, 255)]), + )?; + + Ok(()) +} diff --git a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp index fb4a56ad2070..5cb881053849 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp @@ -21,6 +21,32 @@ namespace rerun::archetypes { /// **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. + /// + /// ## Example + /// + /// ### Log a geospatial line string + /// ```cpp + /// #include + /// + /// int main() { + /// const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); + /// rec.spawn().exit_on_failure(); + /// + /// rerun::Collection line_string = { + /// {41.0000, -109.0452}, + /// {41.0000, -102.0415}, + /// {36.9931, -102.0415}, + /// {36.9931, -109.0452}, + /// {41.0000, -109.0452}}; + /// + /// rec.log( + /// "colorado", + /// rerun::GeoLineStrings({line_string}) + /// .with_radii(rerun::Radius::ui_points(2.0f)) + /// .with_colors(rerun::Color(0, 0, 255)) + /// ); + /// } + /// ``` struct GeoLineStrings { /// The lines strings, expressed in EPSG:4326 coordinates. Collection line_strings; diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py index 2ac891d13396..8baaa2dd6a45 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py @@ -24,6 +24,31 @@ class GeoLineStrings(Archetype): **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. **Note**: Geospatial entities are experimental. + + Example + ------- + ### Log a geospatial line string: + ```python + import rerun as rr + + rr.init("rerun_example_geo_line_strings", spawn=True) + + rr.log( + "colorado", + rr.GeoLineStrings( + [ + [41.0000, -109.0452], + [41.0000, -102.0415], + [36.9931, -102.0415], + [36.9931, -109.0452], + [41.0000, -109.0452], + ], + radii=rr.Radius.ui_points(2.0), + colors=[0, 0, 255], + ), + ) + ``` + """ def __init__( From 67610cc3ede16894a494f3d15e862f3f3b38aec7 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 16:20:24 +0100 Subject: [PATCH 06/28] `py-fmt` --- rerun_py/rerun_sdk/rerun/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rerun_py/rerun_sdk/rerun/__init__.py b/rerun_py/rerun_sdk/rerun/__init__.py index 302faf075a70..6eb3f7d74ae3 100644 --- a/rerun_py/rerun_sdk/rerun/__init__.py +++ b/rerun_py/rerun_sdk/rerun/__init__.py @@ -74,8 +74,8 @@ DisconnectedSpace as DisconnectedSpace, Ellipsoids3D as Ellipsoids3D, EncodedImage as EncodedImage, - GeoPoints as GeoPoints, GeoLineStrings as GeoLineStrings, + GeoPoints as GeoPoints, Image as Image, InstancePoses3D as InstancePoses3D, LineStrips2D as LineStrips2D, From eefbb335eb2cc776ec6e9e79905ebba475933cfc Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 16:27:44 +0100 Subject: [PATCH 07/28] Attempt to fix C++ ci --- docs/snippets/all/archetypes/geo_line_string_simple.cpp | 6 +++--- rerun_cpp/src/rerun.hpp | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.cpp b/docs/snippets/all/archetypes/geo_line_string_simple.cpp index ab3adc2c3168..b4638f424b3c 100644 --- a/docs/snippets/all/archetypes/geo_line_string_simple.cpp +++ b/docs/snippets/all/archetypes/geo_line_string_simple.cpp @@ -6,16 +6,16 @@ int main() { const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); rec.spawn().exit_on_failure(); - rerun::Collection line_string = { + rerun::GeoLineString line_string({ {41.0000, -109.0452}, {41.0000, -102.0415}, {36.9931, -102.0415}, {36.9931, -109.0452}, - {41.0000, -109.0452}}; + {41.0000, -109.0452}}); rec.log( "colorado", - rerun::GeoLineStrings({line_string}) + rerun::GeoLineStrings(line_string) .with_radii(rerun::Radius::ui_points(2.0f)) .with_colors(rerun::Color(0, 0, 255)) ); diff --git a/rerun_cpp/src/rerun.hpp b/rerun_cpp/src/rerun.hpp index a01db0f7765c..275722c3ca15 100644 --- a/rerun_cpp/src/rerun.hpp +++ b/rerun_cpp/src/rerun.hpp @@ -33,6 +33,7 @@ namespace rerun { using components::AlbedoFactor; using components::Color; using components::FillMode; + using components::GeoLineString; using components::HalfSize2D; using components::HalfSize3D; using components::LatLon; From 24c4e8b510db6e438cbcd8420abc859be5793ab7 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 16:28:16 +0100 Subject: [PATCH 08/28] Codegen --- rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp index 5cb881053849..489b9d5d679f 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp @@ -32,16 +32,16 @@ namespace rerun::archetypes { /// const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); /// rec.spawn().exit_on_failure(); /// - /// rerun::Collection line_string = { + /// rerun::GeoLineString line_string({ /// {41.0000, -109.0452}, /// {41.0000, -102.0415}, /// {36.9931, -102.0415}, /// {36.9931, -109.0452}, - /// {41.0000, -109.0452}}; + /// {41.0000, -109.0452}}); /// /// rec.log( /// "colorado", - /// rerun::GeoLineStrings({line_string}) + /// rerun::GeoLineStrings(line_string) /// .with_radii(rerun::Radius::ui_points(2.0f)) /// .with_colors(rerun::Color(0, 0, 255)) /// ); From 3a7d6fcdddba8e1b313f459c99bdfa495ef38678 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 16:51:55 +0100 Subject: [PATCH 09/28] cpp-fmt --- .../all/archetypes/geo_line_string_simple.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.cpp b/docs/snippets/all/archetypes/geo_line_string_simple.cpp index b4638f424b3c..37bfc4b26f3a 100644 --- a/docs/snippets/all/archetypes/geo_line_string_simple.cpp +++ b/docs/snippets/all/archetypes/geo_line_string_simple.cpp @@ -6,12 +6,13 @@ int main() { const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); rec.spawn().exit_on_failure(); - rerun::GeoLineString line_string({ - {41.0000, -109.0452}, - {41.0000, -102.0415}, - {36.9931, -102.0415}, - {36.9931, -109.0452}, - {41.0000, -109.0452}}); + rerun::GeoLineString line_string( + {{41.0000, -109.0452}, + {41.0000, -102.0415}, + {36.9931, -102.0415}, + {36.9931, -109.0452}, + {41.0000, -109.0452}} + ); rec.log( "colorado", From 8f552bbb3d56eab45a532c4ea54a4393b1ae018a Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 16:55:11 +0100 Subject: [PATCH 10/28] more lint fixes --- .../re_space_view_map/src/visualizers/geo_line_strings.rs | 2 +- .../rerun_sdk/rerun/components/geo_line_string_ext.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs index 2b8e1da0eb96..2a496db37be4 100644 --- a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs +++ b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs @@ -21,7 +21,7 @@ struct GeoLineStringsBatch { instance_id: Vec, } -/// Visualizer for [`GeoPoints`]. +/// Visualizer for [`GeoLineString`]. #[derive(Default)] pub struct GeoLineStringsVisualizer { batches: Vec<(EntityPath, GeoLineStringsBatch)>, diff --git a/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py b/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py index 75b77dff280d..20aeffc88776 100644 --- a/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py +++ b/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py @@ -37,7 +37,7 @@ def native_to_pa_array_override(data: GeoLineStringArrayLike, data_type: pa.Data # pure-object elif isinstance(data, GeoLineString): - inners = [DVec2DBatch(data.points).as_arrow_array().storage] + inners = [DVec2DBatch(data.lat_lon).as_arrow_array().storage] # sequences elif isinstance(data, Sequence): @@ -61,9 +61,9 @@ def native_to_pa_array_override(data: GeoLineStringArrayLike, data_type: pa.Data # .. otherwise assume that it's several strips. else: - def to_DVec2D_batch(strip: Any) -> DVec2DBatch: + def to_dvec2D_batch(strip: Any) -> DVec2DBatch: if isinstance(strip, GeoLineString): - return DVec2DBatch(strip.points) + return DVec2DBatch(strip.lat_lon) else: if isinstance(strip, np.ndarray) and (strip.ndim != 2 or strip.shape[1] != 2): raise ValueError( @@ -71,7 +71,7 @@ def to_DVec2D_batch(strip: Any) -> DVec2DBatch: ) return DVec2DBatch(strip) - inners = [to_DVec2D_batch(strip).as_arrow_array().storage for strip in data] + inners = [to_dvec2D_batch(strip).as_arrow_array().storage for strip in data] else: inners = [DVec2DBatch(data).storage] From 68e5a92d278fbae0d0a268adb53753bb1ace787b Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 18:35:36 +0100 Subject: [PATCH 11/28] moar lint --- rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp | 13 +++++++------ rerun_py/docs/gen_common_index.py | 5 ++++- .../release_checklist/check_all_components_ui.py | 6 ++++++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp index 489b9d5d679f..28eb764ef5dd 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp @@ -32,12 +32,13 @@ namespace rerun::archetypes { /// const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); /// rec.spawn().exit_on_failure(); /// - /// rerun::GeoLineString line_string({ - /// {41.0000, -109.0452}, - /// {41.0000, -102.0415}, - /// {36.9931, -102.0415}, - /// {36.9931, -109.0452}, - /// {41.0000, -109.0452}}); + /// rerun::GeoLineString line_string( + /// {{41.0000, -109.0452}, + /// {41.0000, -102.0415}, + /// {36.9931, -102.0415}, + /// {36.9931, -109.0452}, + /// {41.0000, -109.0452}} + /// ); /// /// rec.log( /// "colorado", diff --git a/rerun_py/docs/gen_common_index.py b/rerun_py/docs/gen_common_index.py index 5714a4ab69cd..ade8ef1aaa7d 100755 --- a/rerun_py/docs/gen_common_index.py +++ b/rerun_py/docs/gen_common_index.py @@ -77,7 +77,6 @@ class Section: # This is the list of sections and functions that will be included in the index -# for each of them. SECTION_TABLE: Final[list[Section]] = [ ################################################################################ Section( @@ -213,6 +212,7 @@ class Section: Section( title="Geospatial Archetypes", class_list=[ + "archetypes.GeoLineStrings", "archetypes.GeoPoints", ], gen_page=False, @@ -383,6 +383,9 @@ class Section: ] +# for each of them. + + def is_mentioned(thing: str) -> bool: for section in SECTION_TABLE: if section.class_list is not None: diff --git a/tests/python/release_checklist/check_all_components_ui.py b/tests/python/release_checklist/check_all_components_ui.py index 830bad5efec0..3eb9e976c633 100644 --- a/tests/python/release_checklist/check_all_components_ui.py +++ b/tests/python/release_checklist/check_all_components_ui.py @@ -121,6 +121,12 @@ def alternatives(self) -> list[Any] | None: ), "FillRatioBatch": TestCase(0.5), "GammaCorrectionBatch": TestCase(2.2), + "GeoLineStripBatch": TestCase( + batch=[ + ((0, 0), (1, 1), (2, 2)), + ((3, 3), (4, 4), (5, 5)), + ] + ), "HalfSize2DBatch": TestCase(batch=[(5.0, 10.0), (50, 30), (23, 45)]), "HalfSize3DBatch": TestCase(batch=[(5.0, 10.0, 20.0), (50, 30, 40), (23, 45, 67)]), "ImageBufferBatch": TestCase( From 614a54b9783aa53c9d80692509e650ef5a22f6c2 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 18:40:39 +0100 Subject: [PATCH 12/28] Default line radius 2.0 ui points + revert some messy diff --- .../re_space_view_map/src/visualizers/geo_line_strings.rs | 2 +- rerun_py/docs/gen_common_index.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs index 2a496db37be4..aab84deedc3e 100644 --- a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs +++ b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs @@ -182,7 +182,7 @@ impl TypedComponentFallbackProvider for GeoLineStringsVisualizer { impl TypedComponentFallbackProvider for GeoLineStringsVisualizer { fn fallback_for(&self, _ctx: &QueryContext<'_>) -> Radius { - Radius::from(5.0) + Radius::new_ui_points(2.0) } } diff --git a/rerun_py/docs/gen_common_index.py b/rerun_py/docs/gen_common_index.py index ade8ef1aaa7d..08a5796e3740 100755 --- a/rerun_py/docs/gen_common_index.py +++ b/rerun_py/docs/gen_common_index.py @@ -77,6 +77,7 @@ class Section: # This is the list of sections and functions that will be included in the index +# for each of them. SECTION_TABLE: Final[list[Section]] = [ ################################################################################ Section( @@ -383,9 +384,6 @@ class Section: ] -# for each of them. - - def is_mentioned(thing: str) -> bool: for section in SECTION_TABLE: if section.class_list is not None: From 36eb76899622ee6139a5bc05861946a70953e381 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 18:57:36 +0100 Subject: [PATCH 13/28] Added edit ui for `GeoLineString` (based on `LineStrip2D`) --- .../re_component_ui/src/geo_line_string.rs | 68 +++++++++++++++++++ crates/viewer/re_component_ui/src/lib.rs | 5 ++ .../viewer/re_component_ui/src/line_strip.rs | 5 +- .../check_all_components_ui.py | 2 +- 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 crates/viewer/re_component_ui/src/geo_line_string.rs diff --git a/crates/viewer/re_component_ui/src/geo_line_string.rs b/crates/viewer/re_component_ui/src/geo_line_string.rs new file mode 100644 index 000000000000..42b2e4d62bdb --- /dev/null +++ b/crates/viewer/re_component_ui/src/geo_line_string.rs @@ -0,0 +1,68 @@ +use re_format::{format_f64, format_uint}; +use re_types::components::GeoLineString; +use re_viewer_context::{MaybeMutRef, UiLayout, ViewerContext}; + +use crate::DEFAULT_NUMBER_WIDTH; + +fn singleline_view_geo_line_string( + _ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + value: &mut MaybeMutRef<'_, GeoLineString>, +) -> egui::Response { + UiLayout::List.label( + ui, + format!("{} positions", format_uint(value.as_ref().0.len())), + ) +} + +fn multiline_view_geo_line_string( + _ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + value: &mut MaybeMutRef<'_, GeoLineString>, +) -> egui::Response { + use egui_extras::Column; + + // TODO(andreas): Editing this would be nice! + let value = value.as_ref(); + + // TODO(andreas): Is it really a good idea to always have the full table here? + // Can we use the ui stack to know where we are and do the right thing instead? + UiLayout::SelectionPanelFull + .table(ui) + .resizable(true) + .cell_layout(egui::Layout::left_to_right(egui::Align::Center)) + .columns(Column::initial(DEFAULT_NUMBER_WIDTH).clip(true), 2) + .header(re_ui::DesignTokens::table_header_height(), |mut header| { + re_ui::DesignTokens::setup_table_header(&mut header); + header.col(|ui| { + ui.label("latitude"); + }); + header.col(|ui| { + ui.label("longitude"); + }); + }) + .body(|mut body| { + re_ui::DesignTokens::setup_table_body(&mut body); + let row_height = re_ui::DesignTokens::table_line_height(); + body.rows(row_height, value.0.len(), |mut row| { + if let Some(pos) = value.0.get(row.index()) { + row.col(|ui| { + ui.label(format_f64(pos.x())); + }); + row.col(|ui| { + ui.label(format_f64(pos.y())); + }); + } + }); + }); + + // Placeholder response. + ui.allocate_response(egui::Vec2::ZERO, egui::Sense::hover()) +} + +pub fn register_geo_line_string_component_ui( + registry: &mut re_viewer_context::ComponentUiRegistry, +) { + registry.add_multiline_edit_or_view(multiline_view_geo_line_string); + registry.add_singleline_edit_or_view(singleline_view_geo_line_string); +} diff --git a/crates/viewer/re_component_ui/src/lib.rs b/crates/viewer/re_component_ui/src/lib.rs index 2ea68ea3f4ad..6c7d4c8b0c2a 100644 --- a/crates/viewer/re_component_ui/src/lib.rs +++ b/crates/viewer/re_component_ui/src/lib.rs @@ -7,6 +7,7 @@ mod color; mod datatype_uis; mod entity_path; mod fallback_ui; +mod geo_line_string; mod image_format; mod line_strip; mod map_provider; @@ -46,6 +47,9 @@ use re_types_blueprint::blueprint::components::{ }; use re_viewer_context::gpu_bridge::colormap_edit_or_view_ui; +/// Default number of ui points to show a number. +const DEFAULT_NUMBER_WIDTH: f32 = 52.0; + // ---- /// Crates a component ui registry and registers all editors of this crate to it. @@ -153,6 +157,7 @@ pub fn create_component_ui_registry() -> re_viewer_context::ComponentUiRegistry registry.add_multiline_edit_or_view(pinhole::multiline_view_pinhole); line_strip::register_linestrip_component_ui(&mut registry); + geo_line_string::register_geo_line_string_component_ui(&mut registry); registry.add_singleline_edit_or_view(entity_path::edit_or_view_entity_path); diff --git a/crates/viewer/re_component_ui/src/line_strip.rs b/crates/viewer/re_component_ui/src/line_strip.rs index 5e037378ad26..f168a078a724 100644 --- a/crates/viewer/re_component_ui/src/line_strip.rs +++ b/crates/viewer/re_component_ui/src/line_strip.rs @@ -2,8 +2,7 @@ use re_format::{format_f32, format_uint}; use re_types::components::{LineStrip2D, LineStrip3D}; use re_viewer_context::{MaybeMutRef, UiLayout, ViewerContext}; -/// Default number of ui points to show a number. -const DEFAULT_NUMBER_WIDTH: f32 = 52.0; +use crate::DEFAULT_NUMBER_WIDTH; fn singleline_view_line_strip_3d( _ctx: &ViewerContext<'_>, @@ -94,7 +93,7 @@ fn multiline_view_line_strip_2d( .table(ui) .resizable(true) .cell_layout(egui::Layout::left_to_right(egui::Align::Center)) - .columns(Column::initial(DEFAULT_NUMBER_WIDTH).clip(true), 3) + .columns(Column::initial(DEFAULT_NUMBER_WIDTH).clip(true), 2) .header(re_ui::DesignTokens::table_header_height(), |mut header| { re_ui::DesignTokens::setup_table_header(&mut header); header.col(|ui| { diff --git a/tests/python/release_checklist/check_all_components_ui.py b/tests/python/release_checklist/check_all_components_ui.py index 3eb9e976c633..630eefbe1daa 100644 --- a/tests/python/release_checklist/check_all_components_ui.py +++ b/tests/python/release_checklist/check_all_components_ui.py @@ -121,7 +121,7 @@ def alternatives(self) -> list[Any] | None: ), "FillRatioBatch": TestCase(0.5), "GammaCorrectionBatch": TestCase(2.2), - "GeoLineStripBatch": TestCase( + "GeoLineStringBatch": TestCase( batch=[ ((0, 0), (1, 1), (2, 2)), ((3, 3), (4, 4), (5, 5)), From 1d4cf02762843896516c378b2cdeff4453577bf7 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 19:09:34 +0100 Subject: [PATCH 14/28] Add python unit tests for `GeoPoints` --- rerun_py/tests/unit/common_arrays.py | 42 +++++++++++ rerun_py/tests/unit/test_geopoints.py | 104 ++++++++++++++++++++++++++ rerun_py/tests/unit/test_points2d.py | 2 +- 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 rerun_py/tests/unit/test_geopoints.py diff --git a/rerun_py/tests/unit/common_arrays.py b/rerun_py/tests/unit/common_arrays.py index 5e774e179c08..7edb7235632f 100644 --- a/rerun_py/tests/unit/common_arrays.py +++ b/rerun_py/tests/unit/common_arrays.py @@ -19,6 +19,9 @@ ) from rerun.datatypes import ( Angle, + DVec2D, + DVec2DArrayLike, + DVec2DBatch, Float32ArrayLike, Quaternion, QuaternionArrayLike, @@ -63,6 +66,45 @@ def none_empty_or_value(obj: Any, value: Any) -> Any: return value +dvec2ds_arrays: list[DVec2DArrayLike] = [ + [], + np.array([]), + # Vec2DArrayLike: Sequence[Point2DLike]: + [ + DVec2D([1, 2]), + DVec2D([3, 4]), + ], + # Vec2DArrayLike: Sequence[Point2DLike]: npt.NDArray[np.float64] + [ + np.array([1, 2], dtype=np.float64), + np.array([3, 4], dtype=np.float64), + ], + # Vec2DArrayLike: Sequence[Point2DLike]: Tuple[float, float] + [(1, 2), (3, 4)], + # Vec2DArrayLike: torch.tensor is np.ArrayLike + torch.tensor([(1, 2), (3, 4)], dtype=torch.float64), + # Vec2DArrayLike: Sequence[Point2DLike]: Sequence[float] + [1, 2, 3, 4], + # Vec2DArrayLike: npt.NDArray[np.float64] + np.array([[1, 2], [3, 4]], dtype=np.float64), + # Vec2DArrayLike: npt.NDArray[np.float64] + np.array([1, 2, 3, 4], dtype=np.float64), + # Vec2DArrayLike: npt.NDArray[np.float64] + np.array([1, 2, 3, 4], dtype=np.float64).reshape((2, 2, 1, 1, 1)), + # PyTorch array + torch.asarray([1, 2, 3, 4], dtype=torch.float64), +] + + +def dvec2ds_expected(obj: Any, type_: Any | None = None) -> Any: + if type_ is None: + type_ = DVec2DBatch + + expected = none_empty_or_value(obj, [[1.0, 2.0], [3.0, 4.0]]) + + return type_._optional(expected) + + vec2ds_arrays: list[Vec2DArrayLike] = [ [], np.array([]), diff --git a/rerun_py/tests/unit/test_geopoints.py b/rerun_py/tests/unit/test_geopoints.py new file mode 100644 index 000000000000..d2b46f071175 --- /dev/null +++ b/rerun_py/tests/unit/test_geopoints.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +import itertools +from typing import Optional, cast + +import numpy as np +import pytest +import rerun as rr +from rerun.components import ( + Color, + ColorBatch, + LatLonBatch, +) +from rerun.datatypes import ( + Rgba32ArrayLike, + Float32ArrayLike, + DVec2DArrayLike, +) + +from .common_arrays import ( + colors_arrays, + colors_expected, + dvec2ds_arrays as positions_arrays, + dvec2ds_expected as positions_expected, + radii_arrays, + radii_expected, +) + + +def test_geopoints() -> None: + all_arrays = itertools.zip_longest( + positions_arrays, + radii_arrays, + colors_arrays, + ) + + for positions, radii, colors in all_arrays: + positions = positions if positions is not None else positions_arrays[-1] + + # make Pyright happy as it's apparently not able to track typing info through zip_longest + positions = cast(DVec2DArrayLike, positions) + radii = cast(Optional[Float32ArrayLike], radii) + colors = cast(Optional[Rgba32ArrayLike], colors) + + print(f"rr.GeoPoints(\n {positions}\n radii={radii!r}\n colors={colors!r}\n)") + arch = rr.GeoPoints( + positions, + radii=radii, + colors=colors, + ) + print(f"{arch}\n") + + assert arch.positions == positions_expected(positions, LatLonBatch) + assert arch.radii == radii_expected(radii) + assert arch.colors == colors_expected(colors) + + +@pytest.mark.parametrize( + "data", + [ + [0, 128, 0, 255], + [0, 128, 0], + np.array((0, 128, 0, 255)), + [0.0, 0.5, 0.0, 1.0], + np.array((0.0, 0.5, 0.0, 1.0)), + ], +) +def test_geopoint_single_color(data: Rgba32ArrayLike) -> None: + pts = rr.GeoPoints(np.zeros((5, 2)), colors=data) + + assert pts.colors == ColorBatch(Color([0, 128, 0, 255])) + + +@pytest.mark.parametrize( + "data", + [ + [[0, 128, 0, 255], [128, 0, 0, 255]], + [[0, 128, 0], [128, 0, 0]], + np.array([[0, 128, 0, 255], [128, 0, 0, 255]]), + np.array([0, 128, 0, 255, 128, 0, 0, 255], dtype=np.uint8), + np.array([8388863, 2147483903], dtype=np.uint32), + np.array([[0, 128, 0], [128, 0, 0]]), + [[0.0, 0.5, 0.0, 1.0], [0.5, 0.0, 0.0, 1.0]], + [[0.0, 0.5, 0.0], [0.5, 0.0, 0.0]], + np.array([[0.0, 0.5, 0.0, 1.0], [0.5, 0.0, 0.0, 1.0]]), + np.array([[0.0, 0.5, 0.0], [0.5, 0.0, 0.0]]), + np.array([0.0, 0.5, 0.0, 1.0, 0.5, 0.0, 0.0, 1.0]), + # Note: Sequence[int] is interpreted as a single color when they are 3 or 4 long. For other lengths, they + # are interpreted as list of packed uint32 colors. Note that this means one cannot pass an len=N*4 flat list of + # color components. + [8388863, 2147483903], + ], +) +def test_point2d_multiple_colors(data: Rgba32ArrayLike) -> None: + pts = rr.GeoPoints(np.zeros((5, 2)), colors=data) + + assert pts.colors == ColorBatch([ + Color([0, 128, 0, 255]), + Color([128, 0, 0, 255]), + ]) + + +if __name__ == "__main__": + test_geopoints() diff --git a/rerun_py/tests/unit/test_points2d.py b/rerun_py/tests/unit/test_points2d.py index d1b2e2abfaed..b81d85639636 100644 --- a/rerun_py/tests/unit/test_points2d.py +++ b/rerun_py/tests/unit/test_points2d.py @@ -52,7 +52,7 @@ def test_points2d() -> None: for positions, radii, colors, labels, draw_order, class_ids, keypoint_ids in all_arrays: positions = positions if positions is not None else positions_arrays[-1] - # make Pyright happy as it's apparently not able to track typing info trough zip_longest + # make Pyright happy as it's apparently not able to track typing info through zip_longest positions = cast(Vec2DArrayLike, positions) radii = cast(Optional[Float32ArrayLike], radii) colors = cast(Optional[Rgba32ArrayLike], colors) From 42269cac455b96d8af4f2dcae7fad7d266b490c8 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Fri, 1 Nov 2024 19:20:52 +0100 Subject: [PATCH 15/28] Add python unit tests for `GeoLineStrings` --- rerun_py/tests/unit/test_geo_line_strings.py | 156 +++++++++++++++++++ rerun_py/tests/unit/test_geopoints.py | 4 +- 2 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 rerun_py/tests/unit/test_geo_line_strings.py diff --git a/rerun_py/tests/unit/test_geo_line_strings.py b/rerun_py/tests/unit/test_geo_line_strings.py new file mode 100644 index 000000000000..bb8e3df89da7 --- /dev/null +++ b/rerun_py/tests/unit/test_geo_line_strings.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import itertools +from typing import Any, Optional, cast + +import numpy as np +import pytest +import rerun as rr +import torch +from rerun.components import ( + GeoLineStringArrayLike, + GeoLineStringBatch, +) +from rerun.datatypes import DVec2D, Float32ArrayLike, Rgba32ArrayLike + +from .common_arrays import ( + colors_arrays, + colors_expected, + none_empty_or_value, + radii_arrays, + radii_expected, +) + +geo_line_strings_arrays: list[GeoLineStringArrayLike] = [ + [], + np.array([]), + [ + [[0, 0], [2, 1], [4, -1], [6, 0]], # type: ignore[list-item] + [[0, 3], [1, 4], [2, 2], [3, 4], [4, 2], [5, 4], [6, 3]], # type: ignore[list-item] + ], + [ + [DVec2D([0, 0]), (2, 1), [4, -1], (6, 0)], # type: ignore[list-item] + [DVec2D([0, 3]), (1, 4), [2, 2], (3, 4), [4, 2], (5, 4), [6, 3]], # type: ignore[list-item] + ], + [ + np.array([[0, 0], (2, 1), [4, -1], (6, 0)], dtype=np.float64), + np.array([[0, 3], (1, 4), [2, 2], (3, 4), [4, 2], (5, 4), [6, 3]], dtype=np.float64), + ], + [ + torch.tensor([[0, 0], (2, 1), [4, -1], (6, 0)], dtype=torch.float64), + torch.tensor([[0, 3], (1, 4), [2, 2], (3, 4), [4, 2], (5, 4), [6, 3]], dtype=torch.float64), + ], + # NOTE: Not legal -- non-homogeneous. + # np.array([ + # [[0, 0], (2, 1), [4, -1], (6, 0)], + # [[0, 3], (1, 4), [2, 2], (3, 4), [4, 2], (5, 4), [6, 3]], + # ]), +] + + +def geo_line_strings_expected(obj: Any) -> Any: + expected = none_empty_or_value( + obj, + [ + [[0, 0], [2, 1], [4, -1], [6, 0]], + [[0, 3], [1, 4], [2, 2], [3, 4], [4, 2], [5, 4], [6, 3]], + ], + ) + + return GeoLineStringBatch(expected) + + +def test_geo_line_strings() -> None: + all_arrays = itertools.zip_longest( + geo_line_strings_arrays, + radii_arrays, + colors_arrays, + ) + + for strips, radii, colors in all_arrays: + strips = strips if strips is not None else geo_line_strings_arrays[-1] + + # make Pyright happy as it's apparently not able to track typing info through zip_longest + strips = cast(GeoLineStringArrayLike, strips) + radii = cast(Optional[Float32ArrayLike], radii) + colors = cast(Optional[Rgba32ArrayLike], colors) + + print(f"rr.GeoLineStrings(\n {strips}\n radii={radii!r}\n colors={colors!r}\n)") + arch = rr.GeoLineStrings( + strips, + radii=radii, + colors=colors, + ) + print(f"{arch}\n") + + assert arch.line_strings == geo_line_strings_expected(strips) + assert arch.radii == radii_expected(radii) + assert arch.colors == colors_expected(colors) + + +@pytest.mark.parametrize( + "data", + [ + [[[0, 0], [2, 1]], [[4, -1], [6, 0]]], + np.array([[0, 0], [2, 1], [4, -1], [6, 0]]).reshape([2, 2, 2]), + ], +) +def test_geo_line_strings_segment(data: GeoLineStringArrayLike) -> None: + arch = rr.GeoLineStrings(data) + + assert arch.line_strings == GeoLineStringBatch([ + [[0, 0], [2, 1]], + [[4, -1], [6, 0]], + ]) + + +def test_geo_line_strings_single_line() -> None: + # Regression test for #3643 + # Single line string can be passed and is not interpreted as a batch of zero-sized line strings. + reference = rr.GeoLineStrings([rr.components.GeoLineString([[0, 0], [1, 1]])]) + assert len(reference.line_strings) == 1 + assert reference == rr.GeoLineStrings(rr.components.GeoLineString([[0, 0], [1, 1]])) + assert reference == rr.GeoLineStrings([[[0, 0], [1, 1]]]) + assert reference == rr.GeoLineStrings([[0, 0], [1, 1]]) + assert reference == rr.GeoLineStrings(np.array([[0, 0], [1, 1]])) + assert reference == rr.GeoLineStrings([np.array([0, 0]), np.array([1, 1])]) + + +def test_geo_line_strings_invalid_shapes() -> None: + rr.set_strict_mode(True) + + # We used to support flat arrays but this becomes too ambiguous when passing a single strip. + with pytest.raises(ValueError): + rr.GeoLineStrings( + [ + [0, 0, 2, 1, 4, -1, 6, 0], + [0, 3, 1, 4, 2, 2, 3, 4, 4, 2, 5, 4, 6, 3], + ], + ) + with pytest.raises(ValueError): + rr.GeoLineStrings( + [ + np.array([0, 0, 2, 1, 4, -1, 6, 0], dtype=np.float64), + np.array([0, 3, 1, 4, 2, 2, 3, 4, 4, 2, 5, 4, 6, 3], dtype=np.float64), + ], + ) + + # not homogeneous numpy arrays + with pytest.raises(ValueError): + rr.GeoLineStrings( + np.array([ + [[0, 0], (2, 1), [4, -1], (6, 0)], + [[0, 3], (1, 4), [2, 2], (3, 4), [4, 2], (5, 4), [6, 3]], + ]) + ) + with pytest.raises(ValueError): + rr.GeoLineStrings( + np.array([ + [0, 0, 2, 1, 4, -1, 6, 0], + [0, 3, 1, 4, 2, 2, 3, 4, 4, 2, 5, 4, 6, 3], + ]), + ) + + +if __name__ == "__main__": + test_geo_line_strings() diff --git a/rerun_py/tests/unit/test_geopoints.py b/rerun_py/tests/unit/test_geopoints.py index d2b46f071175..e2b1381e6c63 100644 --- a/rerun_py/tests/unit/test_geopoints.py +++ b/rerun_py/tests/unit/test_geopoints.py @@ -12,9 +12,9 @@ LatLonBatch, ) from rerun.datatypes import ( - Rgba32ArrayLike, - Float32ArrayLike, DVec2DArrayLike, + Float32ArrayLike, + Rgba32ArrayLike, ) from .common_arrays import ( From 1cd5f7664300c8da000db2c4a3c7258f444c35ee Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Mon, 4 Nov 2024 10:03:28 +0100 Subject: [PATCH 16/28] Added url to epsg:4326 + clarified degree & N/E positive for lat/lon --- .../rerun/archetypes/geo_line_strings.fbs | 6 ++++-- .../definitions/rerun/archetypes/geo_points.fbs | 4 ++-- .../rerun/components/geo_line_string.fbs | 2 +- .../definitions/rerun/components/latlon.fbs | 2 +- .../re_types/src/archetypes/geo_line_strings.rs | 6 ++++-- .../store/re_types/src/archetypes/geo_points.rs | 4 ++-- .../re_types/src/components/geo_line_string.rs | 2 +- crates/store/re_types/src/components/lat_lon.rs | 2 +- crates/viewer/re_viewer/src/reflection/mod.rs | 15 ++++++++------- docs/content/reference/types/archetypes.md | 4 ++-- .../types/archetypes/geo_line_strings.md | 4 +++- .../reference/types/archetypes/geo_points.md | 2 +- docs/content/reference/types/components.md | 4 ++-- .../reference/types/components/geo_line_string.md | 2 +- .../content/reference/types/components/lat_lon.md | 2 +- .../src/rerun/archetypes/geo_line_strings.hpp | 6 ++++-- rerun_cpp/src/rerun/archetypes/geo_points.hpp | 4 ++-- .../src/rerun/components/geo_line_string.hpp | 2 +- rerun_cpp/src/rerun/components/lat_lon.hpp | 2 +- .../rerun/archetypes/geo_line_strings.py | 8 +++++--- rerun_py/rerun_sdk/rerun/archetypes/geo_points.py | 6 +++--- .../rerun_sdk/rerun/components/geo_line_string.py | 2 +- rerun_py/rerun_sdk/rerun/components/lat_lon.py | 2 +- 23 files changed, 52 insertions(+), 41 deletions(-) diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs index db5ef8ce7afb..527f347cd68f 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs @@ -2,7 +2,9 @@ namespace rerun.archetypes; // --- -/// Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +/// Geospatial line strings with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. +/// +/// Also known as "line strips" or "polylines". /// /// **Note**: Geospatial entities are experimental. /// @@ -16,7 +18,7 @@ table GeoLineStrings ( ) { // --- Required --- - /// The lines strings, expressed in EPSG:4326 coordinates. + /// The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). line_strings: [rerun.components.GeoLineString] ("attr.rerun.component_required", order: 1000); // --- Recommended --- diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs index 413dca5d490a..7a6b9da56f50 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs @@ -1,6 +1,6 @@ namespace rerun.archetypes; -/// Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +/// Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. /// @@ -14,7 +14,7 @@ table GeoPoints ( ) { // --- Required --- - /// The EPSG:4326 coordinates for the points. + /// The [EPSG:4326](https://epsg.io/4326) coordinates for the points (North/East-positive degrees). positions: [rerun.components.LatLon] ("attr.rerun.component_required", order: 1000); // --- Recommended --- diff --git a/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs b/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs index df905448337e..7023ac594b33 100644 --- a/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs +++ b/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs @@ -3,7 +3,7 @@ namespace rerun.components; // --- -/// A geospatial line string expressed in EPSG:4326 latitude and longitude. +/// A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). table GeoLineString ( "attr.python.aliases": "datatypes.DVec2DArrayLike, npt.NDArray[np.float64]", "attr.python.array_aliases": "npt.NDArray[np.float64]", diff --git a/crates/store/re_types/definitions/rerun/components/latlon.fbs b/crates/store/re_types/definitions/rerun/components/latlon.fbs index 9a7d01dfccae..3d644f54b538 100644 --- a/crates/store/re_types/definitions/rerun/components/latlon.fbs +++ b/crates/store/re_types/definitions/rerun/components/latlon.fbs @@ -3,7 +3,7 @@ namespace rerun.components; // --- -/// A geospatial position expressed in EPSG:4326 latitude and longitude. +/// A geospatial position expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). struct LatLon ( "attr.python.aliases": "npt.NDArray[np.float32], Sequence[float], Tuple[float, float]", "attr.python.array_aliases": "npt.NDArray[np.float32], Sequence[float]", diff --git a/crates/store/re_types/src/archetypes/geo_line_strings.rs b/crates/store/re_types/src/archetypes/geo_line_strings.rs index eba6f051679f..0c3cf26b0022 100644 --- a/crates/store/re_types/src/archetypes/geo_line_strings.rs +++ b/crates/store/re_types/src/archetypes/geo_line_strings.rs @@ -18,7 +18,9 @@ use ::re_types_core::SerializationResult; use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +/// **Archetype**: Geospatial line strings with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. +/// +/// Also known as "line strips" or "polylines". /// /// **Note**: Geospatial entities are experimental. /// @@ -47,7 +49,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// ``` #[derive(Clone, Debug, PartialEq)] pub struct GeoLineStrings { - /// The lines strings, expressed in EPSG:4326 coordinates. + /// The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). pub line_strings: Vec, /// Optional radii for the line strings. diff --git a/crates/store/re_types/src/archetypes/geo_points.rs b/crates/store/re_types/src/archetypes/geo_points.rs index da7ddb0e5aff..f5eb1f4e2226 100644 --- a/crates/store/re_types/src/archetypes/geo_points.rs +++ b/crates/store/re_types/src/archetypes/geo_points.rs @@ -18,7 +18,7 @@ use ::re_types_core::SerializationResult; use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Archetype**: Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +/// **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. /// @@ -50,7 +50,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// #[derive(Clone, Debug, PartialEq)] pub struct GeoPoints { - /// The EPSG:4326 coordinates for the points. + /// The [EPSG:4326](https://epsg.io/4326) coordinates for the points (North/East-positive degrees). pub positions: Vec, /// Optional radii for the points, effectively turning them into circles. diff --git a/crates/store/re_types/src/components/geo_line_string.rs b/crates/store/re_types/src/components/geo_line_string.rs index a62cb2790167..32895478df20 100644 --- a/crates/store/re_types/src/components/geo_line_string.rs +++ b/crates/store/re_types/src/components/geo_line_string.rs @@ -18,7 +18,7 @@ use ::re_types_core::SerializationResult; use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Component**: A geospatial line string expressed in EPSG:4326 latitude and longitude. +/// **Component**: A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). #[derive(Clone, Debug, Default, PartialEq)] #[repr(transparent)] pub struct GeoLineString(pub Vec); diff --git a/crates/store/re_types/src/components/lat_lon.rs b/crates/store/re_types/src/components/lat_lon.rs index 6a35e4e234fd..5f86e5927a3e 100644 --- a/crates/store/re_types/src/components/lat_lon.rs +++ b/crates/store/re_types/src/components/lat_lon.rs @@ -18,7 +18,7 @@ use ::re_types_core::SerializationResult; use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Component**: A geospatial position expressed in EPSG:4326 latitude and longitude. +/// **Component**: A geospatial position expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). #[derive(Clone, Debug, Default, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] #[repr(transparent)] pub struct LatLon(pub crate::datatypes::DVec2D); diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index 6184fd88e090..e40cd5162aaf 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -441,7 +441,7 @@ fn generate_component_reflection() -> Result::name(), ComponentReflection { - docstring_md: "A geospatial line string expressed in EPSG:4326 latitude and longitude.", + docstring_md: "A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees).", custom_placeholder: Some(GeoLineString::default().to_arrow()?), datatype: GeoLineString::arrow_datatype(), }, @@ -497,7 +497,7 @@ fn generate_component_reflection() -> Result::name(), ComponentReflection { - docstring_md: "A geospatial position expressed in EPSG:4326 latitude and longitude.", + docstring_md: "A geospatial position expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees).", custom_placeholder: Some(LatLon::default().to_arrow()?), datatype: LatLon::arrow_datatype(), }, @@ -1252,8 +1252,8 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { ArchetypeFieldReflection { component_name : "rerun.components.GeoLineString".into(), display_name : "Line strings", docstring_md : - "The lines strings, expressed in EPSG:4326 coordinates.", is_required - : true, }, ArchetypeFieldReflection { component_name : + "The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees).", + is_required : true, }, ArchetypeFieldReflection { component_name : "rerun.components.Radius".into(), display_name : "Radii", docstring_md : "Optional radii for the line strings.", is_required : false, }, ArchetypeFieldReflection { component_name : @@ -1270,9 +1270,10 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { fields: vec![ ArchetypeFieldReflection { component_name : "rerun.components.LatLon" .into(), display_name : "Positions", docstring_md : - "The EPSG:4326 coordinates for the points.", is_required : true, }, - ArchetypeFieldReflection { component_name : "rerun.components.Radius" - .into(), display_name : "Radii", docstring_md : + "The [EPSG:4326](https://epsg.io/4326) coordinates for the points (North/East-positive degrees).", + is_required : true, }, ArchetypeFieldReflection { component_name : + "rerun.components.Radius".into(), display_name : "Radii", + docstring_md : "Optional radii for the points, effectively turning them into circles.", is_required : false, }, ArchetypeFieldReflection { component_name : "rerun.components.Color".into(), display_name : "Colors", diff --git a/docs/content/reference/types/archetypes.md b/docs/content/reference/types/archetypes.md index 8a9c26a05f65..42f3169b37e9 100644 --- a/docs/content/reference/types/archetypes.md +++ b/docs/content/reference/types/archetypes.md @@ -14,8 +14,8 @@ This page lists all built-in archetypes. ## Geospatial -* [`GeoLineStrings`](archetypes/geo_line_strings.md): Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. -* [`GeoPoints`](archetypes/geo_points.md): Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +* [`GeoLineStrings`](archetypes/geo_line_strings.md): Geospatial line strings with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. +* [`GeoPoints`](archetypes/geo_points.md): Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. ## Image & tensor diff --git a/docs/content/reference/types/archetypes/geo_line_strings.md b/docs/content/reference/types/archetypes/geo_line_strings.md index 4b87dca1fc85..ae59ddcb6787 100644 --- a/docs/content/reference/types/archetypes/geo_line_strings.md +++ b/docs/content/reference/types/archetypes/geo_line_strings.md @@ -3,7 +3,9 @@ title: "GeoLineStrings" --- -Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +Geospatial line strings with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. + +Also known as "line strips" or "polylines". **Note**: Geospatial entities are experimental. diff --git a/docs/content/reference/types/archetypes/geo_points.md b/docs/content/reference/types/archetypes/geo_points.md index 0b01a9a7d352..21efbb7b18d9 100644 --- a/docs/content/reference/types/archetypes/geo_points.md +++ b/docs/content/reference/types/archetypes/geo_points.md @@ -3,7 +3,7 @@ title: "GeoPoints" --- -Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. +Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. **Note**: Geospatial entities are experimental. diff --git a/docs/content/reference/types/components.md b/docs/content/reference/types/components.md index 8412f5cd7f45..9e0e3f07f681 100644 --- a/docs/content/reference/types/components.md +++ b/docs/content/reference/types/components.md @@ -29,14 +29,14 @@ on [Entities and Components](../../concepts/entity-component.md). * [`FillMode`](components/fill_mode.md): How a geometric shape is drawn and colored. * [`FillRatio`](components/fill_ratio.md): How much a primitive fills out the available space. * [`GammaCorrection`](components/gamma_correction.md): A gamma correction value to be used with a scalar value or color. -* [`GeoLineString`](components/geo_line_string.md): A geospatial line string expressed in EPSG:4326 latitude and longitude. +* [`GeoLineString`](components/geo_line_string.md): A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). * [`HalfSize2D`](components/half_size2d.md): Half-size (radius) of a 2D box. * [`HalfSize3D`](components/half_size3d.md): Half-size (radius) of a 3D box. * [`ImageBuffer`](components/image_buffer.md): A buffer that is known to store image data. * [`ImageFormat`](components/image_format.md): The metadata describing the contents of a [`components.ImageBuffer`](https://rerun.io/docs/reference/types/components/image_buffer). * [`ImagePlaneDistance`](components/image_plane_distance.md): The distance from the camera origin to the image plane when the projection is shown in a 3D viewer. * [`KeypointId`](components/keypoint_id.md): A 16-bit ID representing a type of semantic keypoint within a class. -* [`LatLon`](components/lat_lon.md): A geospatial position expressed in EPSG:4326 latitude and longitude. +* [`LatLon`](components/lat_lon.md): A geospatial position expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). * [`Length`](components/length.md): Length, or one-dimensional size. * [`LineStrip2D`](components/line_strip2d.md): A line strip in 2D space. * [`LineStrip3D`](components/line_strip3d.md): A line strip in 3D space. diff --git a/docs/content/reference/types/components/geo_line_string.md b/docs/content/reference/types/components/geo_line_string.md index e2657ebca7aa..89fab8466b5d 100644 --- a/docs/content/reference/types/components/geo_line_string.md +++ b/docs/content/reference/types/components/geo_line_string.md @@ -3,7 +3,7 @@ title: "GeoLineString" --- -A geospatial line string expressed in EPSG:4326 latitude and longitude. +A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). ## Fields diff --git a/docs/content/reference/types/components/lat_lon.md b/docs/content/reference/types/components/lat_lon.md index a11ad797e89c..ddddd1a1115e 100644 --- a/docs/content/reference/types/components/lat_lon.md +++ b/docs/content/reference/types/components/lat_lon.md @@ -3,7 +3,7 @@ title: "LatLon" --- -A geospatial position expressed in EPSG:4326 latitude and longitude. +A geospatial position expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). ## Rerun datatype [`DVec2D`](../datatypes/dvec2d.md) diff --git a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp index 28eb764ef5dd..40d2cdac9cce 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp @@ -18,7 +18,9 @@ #include namespace rerun::archetypes { - /// **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. + /// **Archetype**: Geospatial line strings with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. + /// + /// Also known as "line strips" or "polylines". /// /// **Note**: Geospatial entities are experimental. /// @@ -49,7 +51,7 @@ namespace rerun::archetypes { /// } /// ``` struct GeoLineStrings { - /// The lines strings, expressed in EPSG:4326 coordinates. + /// The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). Collection line_strings; /// Optional radii for the line strings. diff --git a/rerun_cpp/src/rerun/archetypes/geo_points.hpp b/rerun_cpp/src/rerun/archetypes/geo_points.hpp index 35ddefde2960..02fb6471a174 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_points.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_points.hpp @@ -18,7 +18,7 @@ #include namespace rerun::archetypes { - /// **Archetype**: Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. + /// **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. /// @@ -43,7 +43,7 @@ namespace rerun::archetypes { /// } /// ``` struct GeoPoints { - /// The EPSG:4326 coordinates for the points. + /// The [EPSG:4326](https://epsg.io/4326) coordinates for the points (North/East-positive degrees). Collection positions; /// Optional radii for the points, effectively turning them into circles. diff --git a/rerun_cpp/src/rerun/components/geo_line_string.hpp b/rerun_cpp/src/rerun/components/geo_line_string.hpp index ed1bbfeff415..c2c27cb806fe 100644 --- a/rerun_cpp/src/rerun/components/geo_line_string.hpp +++ b/rerun_cpp/src/rerun/components/geo_line_string.hpp @@ -18,7 +18,7 @@ namespace arrow { } // namespace arrow namespace rerun::components { - /// **Component**: A geospatial line string expressed in EPSG:4326 latitude and longitude. + /// **Component**: A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). struct GeoLineString { rerun::Collection lat_lon; diff --git a/rerun_cpp/src/rerun/components/lat_lon.hpp b/rerun_cpp/src/rerun/components/lat_lon.hpp index 3189887b4ed0..14356ec7d482 100644 --- a/rerun_cpp/src/rerun/components/lat_lon.hpp +++ b/rerun_cpp/src/rerun/components/lat_lon.hpp @@ -11,7 +11,7 @@ #include namespace rerun::components { - /// **Component**: A geospatial position expressed in EPSG:4326 latitude and longitude. + /// **Component**: A geospatial position expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). struct LatLon { rerun::datatypes::DVec2D lat_lon; diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py index 8baaa2dd6a45..a74e349c0cdc 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py @@ -21,7 +21,9 @@ @define(str=False, repr=False, init=False) class GeoLineStrings(Archetype): """ - **Archetype**: Geospatial line strings with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. + **Archetype**: Geospatial line strings with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. + + Also known as "line strips" or "polylines". **Note**: Geospatial entities are experimental. @@ -64,7 +66,7 @@ def __init__( Parameters ---------- line_strings: - The lines strings, expressed in EPSG:4326 coordinates. + The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). radii: Optional radii for the line strings. colors: @@ -100,7 +102,7 @@ def _clear(cls) -> GeoLineStrings: metadata={"component": "required"}, converter=components.GeoLineStringBatch._required, # type: ignore[misc] ) - # The lines strings, expressed in EPSG:4326 coordinates. + # The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). # # (Docstring intentionally commented out to hide this field from the docs) diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py index e83054f56dca..08946f322279 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py @@ -21,7 +21,7 @@ @define(str=False, repr=False, init=False) class GeoPoints(Archetype): """ - **Archetype**: Geospatial points with positions expressed in EPSG:4326 altitude and longitude, and optional colors and radii. + **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. **Note**: Geospatial entities are experimental. @@ -67,7 +67,7 @@ def __init__( Parameters ---------- positions: - The EPSG:4326 coordinates for the points. + The [EPSG:4326](https://epsg.io/4326) coordinates for the points (North/East-positive degrees). radii: Optional radii for the points, effectively turning them into circles. colors: @@ -103,7 +103,7 @@ def _clear(cls) -> GeoPoints: metadata={"component": "required"}, converter=components.LatLonBatch._required, # type: ignore[misc] ) - # The EPSG:4326 coordinates for the points. + # The [EPSG:4326](https://epsg.io/4326) coordinates for the points (North/East-positive degrees). # # (Docstring intentionally commented out to hide this field from the docs) diff --git a/rerun_py/rerun_sdk/rerun/components/geo_line_string.py b/rerun_py/rerun_sdk/rerun/components/geo_line_string.py index e51999590027..df0ae3aa1d7b 100644 --- a/rerun_py/rerun_sdk/rerun/components/geo_line_string.py +++ b/rerun_py/rerun_sdk/rerun/components/geo_line_string.py @@ -26,7 +26,7 @@ @define(init=False) class GeoLineString(GeoLineStringExt, ComponentMixin): - """**Component**: A geospatial line string expressed in EPSG:4326 latitude and longitude.""" + """**Component**: A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees).""" _BATCH_TYPE = None diff --git a/rerun_py/rerun_sdk/rerun/components/lat_lon.py b/rerun_py/rerun_sdk/rerun/components/lat_lon.py index 68f70620033d..f2007858b81e 100644 --- a/rerun_py/rerun_sdk/rerun/components/lat_lon.py +++ b/rerun_py/rerun_sdk/rerun/components/lat_lon.py @@ -15,7 +15,7 @@ class LatLon(datatypes.DVec2D, ComponentMixin): - """**Component**: A geospatial position expressed in EPSG:4326 latitude and longitude.""" + """**Component**: A geospatial position expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees).""" _BATCH_TYPE = None # You can define your own __init__ function as a member of LatLonExt in lat_lon_ext.py From 67aee35a7f282fb4bc00637783063d5c91ec0052 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Mon, 4 Nov 2024 12:24:54 +0100 Subject: [PATCH 17/28] Update C++ and Rust snippet to make constructors more explicit about lat/lon ordering --- .../rerun/archetypes/geo_line_strings.fbs | 1 + .../rerun/archetypes/geo_points.fbs | 1 + .../rerun/components/geo_line_string.fbs | 1 + .../src/archetypes/geo_line_strings.rs | 4 ++-- .../src/archetypes/geo_line_strings_ext.rs | 11 ++++++++++ .../re_types/src/archetypes/geo_points.rs | 6 ++++-- .../re_types/src/archetypes/geo_points_ext.rs | 11 ++++++++++ crates/store/re_types/src/archetypes/mod.rs | 2 ++ .../all/archetypes/geo_line_string_simple.cpp | 4 ++-- .../all/archetypes/geo_line_string_simple.rs | 2 +- .../all/archetypes/geo_point_simple.cpp | 2 +- .../all/archetypes/geo_point_simple.rs | 2 +- .../src/rerun/archetypes/geo_line_strings.hpp | 2 +- rerun_cpp/src/rerun/archetypes/geo_points.hpp | 12 ++++++++++- .../src/rerun/archetypes/geo_points_ext.cpp | 21 +++++++++++++++++++ .../src/rerun/components/geo_line_string.hpp | 19 +++++++++-------- .../rerun/components/geo_line_string_ext.cpp | 21 +++++++++++++++++++ 17 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 crates/store/re_types/src/archetypes/geo_line_strings_ext.rs create mode 100644 crates/store/re_types/src/archetypes/geo_points_ext.rs create mode 100644 rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp create mode 100644 rerun_cpp/src/rerun/components/geo_line_string_ext.cpp diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs index 527f347cd68f..9992377f140d 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs @@ -12,6 +12,7 @@ namespace rerun.archetypes; // TODO(ab): add screenshot table GeoLineStrings ( "attr.rust.derive": "PartialEq", + "attr.rust.new_pub_crate", "attr.docs.category": "Geospatial", "attr.docs.view_types": "MapView", "attr.docs.unreleased" diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs index 7a6b9da56f50..42ddca3e91ce 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs @@ -8,6 +8,7 @@ namespace rerun.archetypes; // TODO(ab): screenshot table GeoPoints ( "attr.rust.derive": "PartialEq", + "attr.rust.new_pub_crate", "attr.docs.category": "Geospatial", "attr.docs.view_types": "MapView", "attr.docs.unreleased" diff --git a/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs b/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs index 7023ac594b33..51da287485ae 100644 --- a/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs +++ b/crates/store/re_types/definitions/rerun/components/geo_line_string.fbs @@ -9,6 +9,7 @@ table GeoLineString ( "attr.python.array_aliases": "npt.NDArray[np.float64]", "attr.rust.derive": "Default, PartialEq", "attr.rust.repr": "transparent", + "attr.cpp.no_field_ctors", "attr.docs.unreleased" ) { lat_lon: [rerun.datatypes.DVec2D] (order: 100); diff --git a/crates/store/re_types/src/archetypes/geo_line_strings.rs b/crates/store/re_types/src/archetypes/geo_line_strings.rs index 0c3cf26b0022..d1db0ffe5858 100644 --- a/crates/store/re_types/src/archetypes/geo_line_strings.rs +++ b/crates/store/re_types/src/archetypes/geo_line_strings.rs @@ -33,7 +33,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// rec.log( /// "colorado", -/// &rerun::GeoLineStrings::new([[ +/// &rerun::GeoLineStrings::from_lat_lon([[ /// [41.0000, -109.0452], /// [41.0000, -102.0415], /// [36.9931, -102.0415], @@ -226,7 +226,7 @@ impl ::re_types_core::ArchetypeReflectionMarker for GeoLineStrings {} impl GeoLineStrings { /// Create a new `GeoLineStrings`. #[inline] - pub fn new( + pub(crate) fn new( line_strings: impl IntoIterator>, ) -> Self { Self { diff --git a/crates/store/re_types/src/archetypes/geo_line_strings_ext.rs b/crates/store/re_types/src/archetypes/geo_line_strings_ext.rs new file mode 100644 index 000000000000..cc795faf33a2 --- /dev/null +++ b/crates/store/re_types/src/archetypes/geo_line_strings_ext.rs @@ -0,0 +1,11 @@ +use super::GeoLineStrings; + +impl GeoLineStrings { + /// Create a new `GeoLineStrings` from [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). + #[inline] + pub fn from_lat_lon( + line_strings: impl IntoIterator>, + ) -> Self { + Self::new(line_strings) + } +} diff --git a/crates/store/re_types/src/archetypes/geo_points.rs b/crates/store/re_types/src/archetypes/geo_points.rs index f5eb1f4e2226..e1474a72eb85 100644 --- a/crates/store/re_types/src/archetypes/geo_points.rs +++ b/crates/store/re_types/src/archetypes/geo_points.rs @@ -31,7 +31,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// /// rec.log( /// "rerun_hq", -/// &rerun::GeoPoints::new([(59.319221, 18.075631)]) +/// &rerun::GeoPoints::from_lat_lon([(59.319221, 18.075631)]) /// .with_radii([rerun::Radius::new_ui_points(10.0)]) /// .with_colors([rerun::Color::from_rgb(255, 0, 0)]), /// )?; @@ -227,7 +227,9 @@ impl ::re_types_core::ArchetypeReflectionMarker for GeoPoints {} impl GeoPoints { /// Create a new `GeoPoints`. #[inline] - pub fn new(positions: impl IntoIterator>) -> Self { + pub(crate) fn new( + positions: impl IntoIterator>, + ) -> Self { Self { positions: positions.into_iter().map(Into::into).collect(), radii: None, diff --git a/crates/store/re_types/src/archetypes/geo_points_ext.rs b/crates/store/re_types/src/archetypes/geo_points_ext.rs new file mode 100644 index 000000000000..22d3a30b196c --- /dev/null +++ b/crates/store/re_types/src/archetypes/geo_points_ext.rs @@ -0,0 +1,11 @@ +use super::GeoPoints; + +impl GeoPoints { + /// Create a new `GeoPoints` from [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). + #[inline] + pub fn from_lat_lon( + positions: impl IntoIterator>, + ) -> Self { + Self::new(positions) + } +} diff --git a/crates/store/re_types/src/archetypes/mod.rs b/crates/store/re_types/src/archetypes/mod.rs index 03084a185d47..8b2c7bc946a1 100644 --- a/crates/store/re_types/src/archetypes/mod.rs +++ b/crates/store/re_types/src/archetypes/mod.rs @@ -24,7 +24,9 @@ mod ellipsoids3d_ext; mod encoded_image; mod encoded_image_ext; mod geo_line_strings; +mod geo_line_strings_ext; mod geo_points; +mod geo_points_ext; mod image; mod image_ext; mod instance_poses3d; diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.cpp b/docs/snippets/all/archetypes/geo_line_string_simple.cpp index 37bfc4b26f3a..651d52caa8d0 100644 --- a/docs/snippets/all/archetypes/geo_line_string_simple.cpp +++ b/docs/snippets/all/archetypes/geo_line_string_simple.cpp @@ -5,8 +5,8 @@ int main() { const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); rec.spawn().exit_on_failure(); - - rerun::GeoLineString line_string( + + auto line_string = rerun::components::GeoLineString::from_lat_lon( {{41.0000, -109.0452}, {41.0000, -102.0415}, {36.9931, -102.0415}, diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.rs b/docs/snippets/all/archetypes/geo_line_string_simple.rs index f9fb9654b576..d29969aa4266 100644 --- a/docs/snippets/all/archetypes/geo_line_string_simple.rs +++ b/docs/snippets/all/archetypes/geo_line_string_simple.rs @@ -5,7 +5,7 @@ fn main() -> Result<(), Box> { rec.log( "colorado", - &rerun::GeoLineStrings::new([[ + &rerun::GeoLineStrings::from_lat_lon([[ [41.0000, -109.0452], [41.0000, -102.0415], [36.9931, -102.0415], diff --git a/docs/snippets/all/archetypes/geo_point_simple.cpp b/docs/snippets/all/archetypes/geo_point_simple.cpp index ed326c5fcdec..30b581f49df0 100644 --- a/docs/snippets/all/archetypes/geo_point_simple.cpp +++ b/docs/snippets/all/archetypes/geo_point_simple.cpp @@ -8,7 +8,7 @@ int main() { rec.log( "rerun_hq", - rerun::GeoPoints({{59.319221, 18.075631}}) + rerun::GeoPoints::from_lat_lon({{59.319221, 18.075631}}) .with_radii(rerun::Radius::ui_points(10.0f)) .with_colors(rerun::Color(255, 0, 0)) ); diff --git a/docs/snippets/all/archetypes/geo_point_simple.rs b/docs/snippets/all/archetypes/geo_point_simple.rs index 60df477a6a41..2659c0c48644 100644 --- a/docs/snippets/all/archetypes/geo_point_simple.rs +++ b/docs/snippets/all/archetypes/geo_point_simple.rs @@ -5,7 +5,7 @@ fn main() -> Result<(), Box> { rec.log( "rerun_hq", - &rerun::GeoPoints::new([(59.319221, 18.075631)]) + &rerun::GeoPoints::from_lat_lon([(59.319221, 18.075631)]) .with_radii([rerun::Radius::new_ui_points(10.0)]) .with_colors([rerun::Color::from_rgb(255, 0, 0)]), )?; diff --git a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp index 40d2cdac9cce..99d293499f37 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp @@ -34,7 +34,7 @@ namespace rerun::archetypes { /// const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); /// rec.spawn().exit_on_failure(); /// - /// rerun::GeoLineString line_string( + /// auto line_string = rerun::components::GeoLineString::from_lat_lon( /// {{41.0000, -109.0452}, /// {41.0000, -102.0415}, /// {36.9931, -102.0415}, diff --git a/rerun_cpp/src/rerun/archetypes/geo_points.hpp b/rerun_cpp/src/rerun/archetypes/geo_points.hpp index 02fb6471a174..05d9ae981cc3 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_points.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_points.hpp @@ -36,7 +36,7 @@ namespace rerun::archetypes { /// /// rec.log( /// "rerun_hq", - /// rerun::GeoPoints({{59.319221, 18.075631}}) + /// rerun::GeoPoints::from_lat_lon({{59.319221, 18.075631}}) /// .with_radii(rerun::Radius::ui_points(10.0f)) /// .with_colors(rerun::Color(255, 0, 0)) /// ); @@ -59,6 +59,16 @@ namespace rerun::archetypes { /// Indicator component, used to identify the archetype when converting to a list of components. using IndicatorComponent = rerun::components::IndicatorComponent; + public: // START of extensions from geo_points_ext.cpp: + /// Creates a new GeoPoints object based on EPSG:4326 latitude and longitude (North/East-positive degrees). + static GeoPoints from_lat_lon(Collection positions_) { + GeoPoints points; + points.positions = std::move(positions_); + return points; + } + + // END of extensions from geo_points_ext.cpp, start of generated code: + public: GeoPoints() = default; GeoPoints(GeoPoints&& other) = default; diff --git a/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp b/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp new file mode 100644 index 000000000000..87c198afe6c5 --- /dev/null +++ b/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp @@ -0,0 +1,21 @@ +#include "geo_points.hpp" + +// #define EDIT_EXTENSION + +namespace rerun { + namespace archetypes { + +#ifdef EDIT_EXTENSION + // + + /// Creates a new GeoPoints object based on EPSG:4326 latitude and longitude (North/East-positive degrees). + static GeoPoints from_lat_lon(Collection positions_) { + GeoPoints points; + points.positions = std::move(positions_); + return points; + } + + // +#endif + } // namespace archetypes +} // namespace rerun \ No newline at end of file diff --git a/rerun_cpp/src/rerun/components/geo_line_string.hpp b/rerun_cpp/src/rerun/components/geo_line_string.hpp index c2c27cb806fe..d7d3a61a0a6b 100644 --- a/rerun_cpp/src/rerun/components/geo_line_string.hpp +++ b/rerun_cpp/src/rerun/components/geo_line_string.hpp @@ -9,7 +9,6 @@ #include #include -#include namespace arrow { class Array; @@ -22,16 +21,18 @@ namespace rerun::components { struct GeoLineString { rerun::Collection lat_lon; - public: - GeoLineString() = default; + public: // START of extensions from geo_line_string_ext.cpp: + /// Creates a new GeoLineString object based on EPSG:4326 latitude and longitude (North/East-positive degrees). + static GeoLineString from_lat_lon(Collection lat_lon_) { + GeoLineString line_string; + line_string.lat_lon = std::move(lat_lon_); + return line_string; + } - GeoLineString(rerun::Collection lat_lon_) - : lat_lon(std::move(lat_lon_)) {} + // END of extensions from geo_line_string_ext.cpp, start of generated code: - GeoLineString& operator=(rerun::Collection lat_lon_) { - lat_lon = std::move(lat_lon_); - return *this; - } + public: + GeoLineString() = default; }; } // namespace rerun::components diff --git a/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp b/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp new file mode 100644 index 000000000000..0ceebf66ee42 --- /dev/null +++ b/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp @@ -0,0 +1,21 @@ +#include "geo_line_string.hpp" + +// #define EDIT_EXTENSION + +namespace rerun { + namespace components { + +#ifdef EDIT_EXTENSION + // + + /// Creates a new GeoLineString object based on EPSG:4326 latitude and longitude (North/East-positive degrees). + static GeoLineString from_lat_lon(Collection lat_lon_) { + GeoLineString line_string; + line_string.lat_lon = std::move(lat_lon_); + return line_string; + } + + // +#endif + } // namespace archetypes +} // namespace rerun ∆ \ No newline at end of file From cbb267a658b031574744dfbef691c5626a305213 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Mon, 4 Nov 2024 15:07:18 +0100 Subject: [PATCH 18/28] Add explicit `from_lat_lon` and `from_lon_lat` to `GeoPoints` in Python --- .../all/archetypes/geo_line_string_simple.cpp | 2 +- .../all/archetypes/geo_point_simple.py | 2 +- .../src/rerun/archetypes/geo_points_ext.cpp | 2 +- .../rerun/components/geo_line_string_ext.cpp | 4 +- .../rerun_sdk/rerun/archetypes/geo_points.py | 5 +- .../rerun/archetypes/geo_points_ext.py | 86 +++++++++++++++++++ rerun_py/tests/unit/test_geopoints.py | 12 +++ 7 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 rerun_py/rerun_sdk/rerun/archetypes/geo_points_ext.py diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.cpp b/docs/snippets/all/archetypes/geo_line_string_simple.cpp index 651d52caa8d0..0bf1a43daf5a 100644 --- a/docs/snippets/all/archetypes/geo_line_string_simple.cpp +++ b/docs/snippets/all/archetypes/geo_line_string_simple.cpp @@ -5,7 +5,7 @@ int main() { const auto rec = rerun::RecordingStream("rerun_example_geo_line_strings"); rec.spawn().exit_on_failure(); - + auto line_string = rerun::components::GeoLineString::from_lat_lon( {{41.0000, -109.0452}, {41.0000, -102.0415}, diff --git a/docs/snippets/all/archetypes/geo_point_simple.py b/docs/snippets/all/archetypes/geo_point_simple.py index f30e7db8944c..70e44625a3cc 100644 --- a/docs/snippets/all/archetypes/geo_point_simple.py +++ b/docs/snippets/all/archetypes/geo_point_simple.py @@ -6,7 +6,7 @@ rr.log( "rerun_hq", - rr.GeoPoints( + rr.GeoPoints.from_lat_lon( [59.319221, 18.075631], radii=rr.Radius.ui_points(10.0), colors=[255, 0, 0], diff --git a/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp b/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp index 87c198afe6c5..25c9eeebff52 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp +++ b/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp @@ -18,4 +18,4 @@ namespace rerun { // #endif } // namespace archetypes -} // namespace rerun \ No newline at end of file +} // namespace rerun diff --git a/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp b/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp index 0ceebf66ee42..469c7a8e45bb 100644 --- a/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp +++ b/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp @@ -17,5 +17,5 @@ namespace rerun { // #endif - } // namespace archetypes -} // namespace rerun ∆ \ No newline at end of file + } // namespace components +} // namespace rerun diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py index 08946f322279..8295d0f244f2 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py @@ -14,12 +14,13 @@ Archetype, ) from ..error_utils import catch_and_log_exceptions +from .geo_points_ext import GeoPointsExt __all__ = ["GeoPoints"] @define(str=False, repr=False, init=False) -class GeoPoints(Archetype): +class GeoPoints(GeoPointsExt, Archetype): """ **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. @@ -35,7 +36,7 @@ class GeoPoints(Archetype): rr.log( "rerun_hq", - rr.GeoPoints( + rr.GeoPoints.from_lat_lon( [59.319221, 18.075631], radii=rr.Radius.ui_points(10.0), colors=[255, 0, 0], diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_points_ext.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_points_ext.py new file mode 100644 index 000000000000..a0e85b1484a0 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_points_ext.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Sequence + +import numpy as np + +from .. import datatypes +from .._converters import to_np_float64 + +if TYPE_CHECKING: + from .. import GeoPoints + +NUMPY_VERSION = tuple(map(int, np.version.version.split(".")[:2])) + + +class GeoPointsExt: + """Extension for [GeoPoints][rerun.archetypes.GeoPoints].""" + + @staticmethod + def from_lat_lon( + positions: datatypes.DVec2DArrayLike, + *, + radii: datatypes.Float32ArrayLike | None = None, + colors: datatypes.Rgba32ArrayLike | None = None, + ) -> GeoPoints: + """ + Create a new instance of the GeoPoints archetype using latitudes and longitudes, in that order. + + *Note*: this is how Rerun natively stores geospatial data. + + Parameters + ---------- + positions: + The [EPSG:4326](https://epsg.io/4326) latitudes and longitudes (in that order) coordinates for the points (North/East-positive degrees). + radii: + Optional radii for the points, effectively turning them into circles. + colors: + Optional colors for the points. + + The colors are interpreted as RGB or RGBA in sRGB gamma-space, + As either 0-1 floats or 0-255 integers, with separate alpha. + + """ + + from .. import GeoPoints + + return GeoPoints(positions, radii=radii, colors=colors) + + @staticmethod + def from_lon_lat( + positions: datatypes.DVec2DArrayLike, + *, + radii: datatypes.Float32ArrayLike | None = None, + colors: datatypes.Rgba32ArrayLike | None = None, + ) -> GeoPoints: + """ + Create a new instance of the GeoPoints archetype using longitude and latitudes, in that order. + + *Note*: Rerun stores latitude first, so this method converts the input to a Numpy array and swaps the + coordinates first. + + Parameters + ---------- + positions: + The [EPSG:4326](https://epsg.io/4326) latitudes and longitudes (in that order) coordinates for the points (North/East-positive degrees). + radii: + Optional radii for the points, effectively turning them into circles. + colors: + Optional colors for the points. + + The colors are interpreted as RGB or RGBA in sRGB gamma-space, + As either 0-1 floats or 0-255 integers, with separate alpha. + + """ + + from .. import GeoPoints + from ..datatypes import DVec2D + + if isinstance(positions, Sequence): + flipped_pos = np.array([np.array(p.xy) if isinstance(p, DVec2D) else p for p in positions]) + elif isinstance(positions, DVec2D): + flipped_pos = np.array(positions.xy) + else: + flipped_pos = to_np_float64(positions) + + return GeoPoints(np.fliplr(flipped_pos), radii=radii, colors=colors) diff --git a/rerun_py/tests/unit/test_geopoints.py b/rerun_py/tests/unit/test_geopoints.py index e2b1381e6c63..95c626f290ee 100644 --- a/rerun_py/tests/unit/test_geopoints.py +++ b/rerun_py/tests/unit/test_geopoints.py @@ -55,6 +55,18 @@ def test_geopoints() -> None: assert arch.colors == colors_expected(colors) +def test_geopoints_lat_lon_constructors() -> None: + positions_lat_lon = np.array([[59.319221, 18.075631], [50.319221, 12.075631]]) + positions_lon_lat = np.fliplr(positions_lat_lon) + + arch1 = rr.GeoPoints.from_lat_lon(positions_lat_lon) + arch2 = rr.GeoPoints.from_lon_lat(positions_lon_lat) + arch3 = rr.GeoPoints(positions_lat_lon) + + assert arch1.positions == arch2.positions + assert arch2.positions == arch3.positions + + @pytest.mark.parametrize( "data", [ From d9690738a22d6c46aed748c6d26829e5318fb605 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Mon, 4 Nov 2024 16:16:59 +0100 Subject: [PATCH 19/28] All Python constructor now have `lat_lon=` kw-only argument + revert the custom constructors --- .../rerun/archetypes/geo_line_strings.fbs | 4 +- .../rerun/archetypes/geo_points.fbs | 2 +- .../src/archetypes/geo_line_strings.rs | 6 +- .../re_types/src/archetypes/geo_points.rs | 2 +- crates/viewer/re_viewer/src/reflection/mod.rs | 4 +- docs/content/reference/types/archetypes.md | 2 +- .../reference/types/archetypes/geo_points.md | 2 +- .../all/archetypes/geo_line_string_simple.py | 2 +- .../all/archetypes/geo_point_simple.py | 4 +- .../src/rerun/archetypes/geo_line_strings.hpp | 6 +- rerun_cpp/src/rerun/archetypes/geo_points.hpp | 2 +- .../rerun/archetypes/geo_line_strings.py | 66 ++++++++--------- .../rerun/archetypes/geo_line_strings_ext.py | 41 +++++++++++ .../rerun_sdk/rerun/archetypes/geo_points.py | 37 +--------- .../rerun/archetypes/geo_points_ext.py | 70 +++++-------------- .../rerun/components/geo_line_string.py | 7 +- .../rerun/components/geo_line_string_ext.py | 9 ++- rerun_py/tests/unit/test_geo_line_strings.py | 26 +++---- rerun_py/tests/unit/test_geopoints.py | 20 ++---- 19 files changed, 134 insertions(+), 178 deletions(-) create mode 100644 rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings_ext.py diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs index 9992377f140d..11f8397603a5 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_line_strings.fbs @@ -19,7 +19,7 @@ table GeoLineStrings ( ) { // --- Required --- - /// The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). + /// The line strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). line_strings: [rerun.components.GeoLineString] ("attr.rerun.component_required", order: 1000); // --- Recommended --- @@ -27,7 +27,7 @@ table GeoLineStrings ( /// Optional radii for the line strings. radii: [rerun.components.Radius] ("attr.rerun.component_recommended", nullable, order: 2000); - /// Optional colors for the linestrings. + /// Optional colors for the line strings. /// /// \py The colors are interpreted as RGB or RGBA in sRGB gamma-space, /// \py As either 0-1 floats or 0-255 integers, with separate alpha. diff --git a/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs b/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs index 42ddca3e91ce..462a14191e37 100644 --- a/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs +++ b/crates/store/re_types/definitions/rerun/archetypes/geo_points.fbs @@ -1,6 +1,6 @@ namespace rerun.archetypes; -/// Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. +/// Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees), and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. /// diff --git a/crates/store/re_types/src/archetypes/geo_line_strings.rs b/crates/store/re_types/src/archetypes/geo_line_strings.rs index d1db0ffe5858..e19eae97a267 100644 --- a/crates/store/re_types/src/archetypes/geo_line_strings.rs +++ b/crates/store/re_types/src/archetypes/geo_line_strings.rs @@ -49,13 +49,13 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; /// ``` #[derive(Clone, Debug, PartialEq)] pub struct GeoLineStrings { - /// The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). + /// The line strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). pub line_strings: Vec, /// Optional radii for the line strings. pub radii: Option>, - /// Optional colors for the linestrings. + /// Optional colors for the line strings. pub colors: Option>, } @@ -246,7 +246,7 @@ impl GeoLineStrings { self } - /// Optional colors for the linestrings. + /// Optional colors for the line strings. #[inline] pub fn with_colors( mut self, diff --git a/crates/store/re_types/src/archetypes/geo_points.rs b/crates/store/re_types/src/archetypes/geo_points.rs index e1474a72eb85..3a7cabc666cd 100644 --- a/crates/store/re_types/src/archetypes/geo_points.rs +++ b/crates/store/re_types/src/archetypes/geo_points.rs @@ -18,7 +18,7 @@ use ::re_types_core::SerializationResult; use ::re_types_core::{ComponentBatch, MaybeOwnedComponentBatch}; use ::re_types_core::{DeserializationError, DeserializationResult}; -/// **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. +/// **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees), and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. /// diff --git a/crates/viewer/re_viewer/src/reflection/mod.rs b/crates/viewer/re_viewer/src/reflection/mod.rs index e40cd5162aaf..c76fc051e2ce 100644 --- a/crates/viewer/re_viewer/src/reflection/mod.rs +++ b/crates/viewer/re_viewer/src/reflection/mod.rs @@ -1252,13 +1252,13 @@ fn generate_archetype_reflection() -> ArchetypeReflectionMap { ArchetypeFieldReflection { component_name : "rerun.components.GeoLineString".into(), display_name : "Line strings", docstring_md : - "The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees).", + "The line strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees).", is_required : true, }, ArchetypeFieldReflection { component_name : "rerun.components.Radius".into(), display_name : "Radii", docstring_md : "Optional radii for the line strings.", is_required : false, }, ArchetypeFieldReflection { component_name : "rerun.components.Color".into(), display_name : "Colors", - docstring_md : "Optional colors for the linestrings.", is_required : + docstring_md : "Optional colors for the line strings.", is_required : false, }, ], }, diff --git a/docs/content/reference/types/archetypes.md b/docs/content/reference/types/archetypes.md index 42f3169b37e9..65e30ca822b4 100644 --- a/docs/content/reference/types/archetypes.md +++ b/docs/content/reference/types/archetypes.md @@ -15,7 +15,7 @@ This page lists all built-in archetypes. ## Geospatial * [`GeoLineStrings`](archetypes/geo_line_strings.md): Geospatial line strings with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. -* [`GeoPoints`](archetypes/geo_points.md): Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. +* [`GeoPoints`](archetypes/geo_points.md): Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees), and optional colors and radii. ## Image & tensor diff --git a/docs/content/reference/types/archetypes/geo_points.md b/docs/content/reference/types/archetypes/geo_points.md index 21efbb7b18d9..1d83565979fc 100644 --- a/docs/content/reference/types/archetypes/geo_points.md +++ b/docs/content/reference/types/archetypes/geo_points.md @@ -3,7 +3,7 @@ title: "GeoPoints" --- -Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. +Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees), and optional colors and radii. **Note**: Geospatial entities are experimental. diff --git a/docs/snippets/all/archetypes/geo_line_string_simple.py b/docs/snippets/all/archetypes/geo_line_string_simple.py index 2485fcfd518f..c4ec6f9a0b31 100644 --- a/docs/snippets/all/archetypes/geo_line_string_simple.py +++ b/docs/snippets/all/archetypes/geo_line_string_simple.py @@ -7,7 +7,7 @@ rr.log( "colorado", rr.GeoLineStrings( - [ + lat_lon=[ [41.0000, -109.0452], [41.0000, -102.0415], [36.9931, -102.0415], diff --git a/docs/snippets/all/archetypes/geo_point_simple.py b/docs/snippets/all/archetypes/geo_point_simple.py index 70e44625a3cc..0778f0bad2e4 100644 --- a/docs/snippets/all/archetypes/geo_point_simple.py +++ b/docs/snippets/all/archetypes/geo_point_simple.py @@ -6,8 +6,8 @@ rr.log( "rerun_hq", - rr.GeoPoints.from_lat_lon( - [59.319221, 18.075631], + rr.GeoPoints( + lat_lon=[59.319221, 18.075631], radii=rr.Radius.ui_points(10.0), colors=[255, 0, 0], ), diff --git a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp index 99d293499f37..dbd48a1fcd1a 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_line_strings.hpp @@ -51,13 +51,13 @@ namespace rerun::archetypes { /// } /// ``` struct GeoLineStrings { - /// The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). + /// The line strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). Collection line_strings; /// Optional radii for the line strings. std::optional> radii; - /// Optional colors for the linestrings. + /// Optional colors for the line strings. std::optional> colors; public: @@ -81,7 +81,7 @@ namespace rerun::archetypes { RR_WITH_MAYBE_UNINITIALIZED_DISABLED(return std::move(*this);) } - /// Optional colors for the linestrings. + /// Optional colors for the line strings. GeoLineStrings with_colors(Collection _colors) && { colors = std::move(_colors); // See: https://github.com/rerun-io/rerun/issues/4027 diff --git a/rerun_cpp/src/rerun/archetypes/geo_points.hpp b/rerun_cpp/src/rerun/archetypes/geo_points.hpp index 05d9ae981cc3..14d06e2579a5 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_points.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_points.hpp @@ -18,7 +18,7 @@ #include namespace rerun::archetypes { - /// **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. + /// **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees), and optional colors and radii. /// /// **Note**: Geospatial entities are experimental. /// diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py index a74e349c0cdc..dfc2b51b66fe 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py @@ -5,21 +5,19 @@ from __future__ import annotations -from typing import Any - from attrs import define, field -from .. import components, datatypes +from .. import components from .._baseclasses import ( Archetype, ) -from ..error_utils import catch_and_log_exceptions +from .geo_line_strings_ext import GeoLineStringsExt __all__ = ["GeoLineStrings"] @define(str=False, repr=False, init=False) -class GeoLineStrings(Archetype): +class GeoLineStrings(GeoLineStringsExt, Archetype): """ **Archetype**: Geospatial line strings with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. @@ -49,39 +47,35 @@ class GeoLineStrings(Archetype): colors=[0, 0, 255], ), ) + + rr.log( + "colorado", + rr.GeoLineStrings( + lat_lon=[ + [ + [41.0000, -109.0452], + [41.0000, -102.0415], + [36.9931, -102.0415], + [36.9931, -109.0452], + [41.0000, -109.0452], + ], + rr.components.GeoLineString( + lat_lon=[ + [41.0000, -109.0452], + [36.9931, -109.0452], + [41.0000, -109.0452], + ] + ), + ], + radii=rr.Radius.ui_points(2.0), + colors=[0, 0, 255], + ), + ) ``` """ - def __init__( - self: Any, - line_strings: components.GeoLineStringArrayLike, - *, - radii: datatypes.Float32ArrayLike | None = None, - colors: datatypes.Rgba32ArrayLike | None = None, - ): - """ - Create a new instance of the GeoLineStrings archetype. - - Parameters - ---------- - line_strings: - The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). - radii: - Optional radii for the line strings. - colors: - Optional colors for the linestrings. - - The colors are interpreted as RGB or RGBA in sRGB gamma-space, - As either 0-1 floats or 0-255 integers, with separate alpha. - - """ - - # You can define your own __init__ function as a member of GeoLineStringsExt in geo_line_strings_ext.py - with catch_and_log_exceptions(context=self.__class__.__name__): - self.__attrs_init__(line_strings=line_strings, radii=radii, colors=colors) - return - self.__attrs_clear__() + # __init__ can be found in geo_line_strings_ext.py def __attrs_clear__(self) -> None: """Convenience method for calling `__attrs_init__` with all `None`s.""" @@ -102,7 +96,7 @@ def _clear(cls) -> GeoLineStrings: metadata={"component": "required"}, converter=components.GeoLineStringBatch._required, # type: ignore[misc] ) - # The lines strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). + # The line strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). # # (Docstring intentionally commented out to hide this field from the docs) @@ -120,7 +114,7 @@ def _clear(cls) -> GeoLineStrings: default=None, converter=components.ColorBatch._optional, # type: ignore[misc] ) - # Optional colors for the linestrings. + # Optional colors for the line strings. # # The colors are interpreted as RGB or RGBA in sRGB gamma-space, # As either 0-1 floats or 0-255 integers, with separate alpha. diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings_ext.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings_ext.py new file mode 100644 index 000000000000..5b4841bee89a --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings_ext.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from typing import Any + +from .. import components, datatypes +from ..error_utils import catch_and_log_exceptions + + +class GeoLineStringsExt: + """Extension for [GeoLineStrings][rerun.archetypes.GeoLineStrings].""" + + # TODO(ab): the purpose of this override is to rename the required parameter and make it keyword-only. Should be codegen-able? + def __init__( + self: Any, + *, + lat_lon: components.GeoLineStringArrayLike, + radii: datatypes.Float32ArrayLike | None = None, + colors: datatypes.Rgba32ArrayLike | None = None, + ): + """ + Create a new instance of the GeoLineStrings archetype. + + Parameters + ---------- + lat_lon: + The line strings, expressed in [EPSG:4326](https://epsg.io/4326) coordinates (North/East-positive degrees). + radii: + Optional radii for the line strings. + colors: + Optional colors for the linestrings. + + The colors are interpreted as RGB or RGBA in sRGB gamma-space, + As either 0-1 floats or 0-255 integers, with separate alpha. + + """ + + # You can define your own __init__ function as a member of GeoLineStringsExt in geo_line_strings_ext.py + with catch_and_log_exceptions(context=self.__class__.__name__): + self.__attrs_init__(line_strings=lat_lon, radii=radii, colors=colors) + return + self.__attrs_clear__() diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py index 8295d0f244f2..b8972a64705b 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py @@ -5,15 +5,12 @@ from __future__ import annotations -from typing import Any - from attrs import define, field -from .. import components, datatypes +from .. import components from .._baseclasses import ( Archetype, ) -from ..error_utils import catch_and_log_exceptions from .geo_points_ext import GeoPointsExt __all__ = ["GeoPoints"] @@ -22,7 +19,7 @@ @define(str=False, repr=False, init=False) class GeoPoints(GeoPointsExt, Archetype): """ - **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) altitude and longitude (North/East-positive degrees), and optional colors and radii. + **Archetype**: Geospatial points with positions expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees), and optional colors and radii. **Note**: Geospatial entities are experimental. @@ -55,35 +52,7 @@ class GeoPoints(GeoPointsExt, Archetype): """ - def __init__( - self: Any, - positions: datatypes.DVec2DArrayLike, - *, - radii: datatypes.Float32ArrayLike | None = None, - colors: datatypes.Rgba32ArrayLike | None = None, - ): - """ - Create a new instance of the GeoPoints archetype. - - Parameters - ---------- - positions: - The [EPSG:4326](https://epsg.io/4326) coordinates for the points (North/East-positive degrees). - radii: - Optional radii for the points, effectively turning them into circles. - colors: - Optional colors for the points. - - The colors are interpreted as RGB or RGBA in sRGB gamma-space, - As either 0-1 floats or 0-255 integers, with separate alpha. - - """ - - # You can define your own __init__ function as a member of GeoPointsExt in geo_points_ext.py - with catch_and_log_exceptions(context=self.__class__.__name__): - self.__attrs_init__(positions=positions, radii=radii, colors=colors) - return - self.__attrs_clear__() + # __init__ can be found in geo_points_ext.py def __attrs_clear__(self) -> None: """Convenience method for calling `__attrs_init__` with all `None`s.""" diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_points_ext.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_points_ext.py index a0e85b1484a0..dd897b81c8a4 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_points_ext.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_points_ext.py @@ -1,14 +1,14 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence +from typing import TYPE_CHECKING, Any import numpy as np from .. import datatypes -from .._converters import to_np_float64 +from ..error_utils import catch_and_log_exceptions if TYPE_CHECKING: - from .. import GeoPoints + pass NUMPY_VERSION = tuple(map(int, np.version.version.split(".")[:2])) @@ -16,22 +16,21 @@ class GeoPointsExt: """Extension for [GeoPoints][rerun.archetypes.GeoPoints].""" - @staticmethod - def from_lat_lon( - positions: datatypes.DVec2DArrayLike, + # TODO(ab): the purpose of this override is to rename the required parameter and make it keyword-only. Should be codegen-able? + def __init__( + self: Any, *, + lat_lon: datatypes.DVec2DArrayLike, radii: datatypes.Float32ArrayLike | None = None, colors: datatypes.Rgba32ArrayLike | None = None, - ) -> GeoPoints: + ): """ - Create a new instance of the GeoPoints archetype using latitudes and longitudes, in that order. - - *Note*: this is how Rerun natively stores geospatial data. + Create a new instance of the GeoPoints archetype. Parameters ---------- - positions: - The [EPSG:4326](https://epsg.io/4326) latitudes and longitudes (in that order) coordinates for the points (North/East-positive degrees). + lat_lon: + The [EPSG:4326](https://epsg.io/4326) coordinates for the points (North/East-positive degrees). radii: Optional radii for the points, effectively turning them into circles. colors: @@ -42,45 +41,8 @@ def from_lat_lon( """ - from .. import GeoPoints - - return GeoPoints(positions, radii=radii, colors=colors) - - @staticmethod - def from_lon_lat( - positions: datatypes.DVec2DArrayLike, - *, - radii: datatypes.Float32ArrayLike | None = None, - colors: datatypes.Rgba32ArrayLike | None = None, - ) -> GeoPoints: - """ - Create a new instance of the GeoPoints archetype using longitude and latitudes, in that order. - - *Note*: Rerun stores latitude first, so this method converts the input to a Numpy array and swaps the - coordinates first. - - Parameters - ---------- - positions: - The [EPSG:4326](https://epsg.io/4326) latitudes and longitudes (in that order) coordinates for the points (North/East-positive degrees). - radii: - Optional radii for the points, effectively turning them into circles. - colors: - Optional colors for the points. - - The colors are interpreted as RGB or RGBA in sRGB gamma-space, - As either 0-1 floats or 0-255 integers, with separate alpha. - - """ - - from .. import GeoPoints - from ..datatypes import DVec2D - - if isinstance(positions, Sequence): - flipped_pos = np.array([np.array(p.xy) if isinstance(p, DVec2D) else p for p in positions]) - elif isinstance(positions, DVec2D): - flipped_pos = np.array(positions.xy) - else: - flipped_pos = to_np_float64(positions) - - return GeoPoints(np.fliplr(flipped_pos), radii=radii, colors=colors) + # You can define your own __init__ function as a member of GeoPointsExt in geo_points_ext.py + with catch_and_log_exceptions(context=self.__class__.__name__): + self.__attrs_init__(positions=lat_lon, radii=radii, colors=colors) + return + self.__attrs_clear__() diff --git a/rerun_py/rerun_sdk/rerun/components/geo_line_string.py b/rerun_py/rerun_sdk/rerun/components/geo_line_string.py index df0ae3aa1d7b..7662e8cc12a6 100644 --- a/rerun_py/rerun_sdk/rerun/components/geo_line_string.py +++ b/rerun_py/rerun_sdk/rerun/components/geo_line_string.py @@ -29,12 +29,7 @@ class GeoLineString(GeoLineStringExt, ComponentMixin): """**Component**: A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees).""" _BATCH_TYPE = None - - def __init__(self: Any, lat_lon: GeoLineStringLike): - """Create a new instance of the GeoLineString component.""" - - # You can define your own __init__ function as a member of GeoLineStringExt in geo_line_string_ext.py - self.__attrs_init__(lat_lon=lat_lon) + # __init__ can be found in geo_line_string_ext.py lat_lon: list[datatypes.DVec2D] = field() diff --git a/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py b/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py index 20aeffc88776..1b0311388108 100644 --- a/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py +++ b/rerun_py/rerun_sdk/rerun/components/geo_line_string_ext.py @@ -8,7 +8,7 @@ import pyarrow as pa if TYPE_CHECKING: - from . import GeoLineStringArrayLike + from . import GeoLineStringArrayLike, GeoLineStringLike def next_offset(acc: int, arr: Sized) -> int: @@ -18,6 +18,13 @@ def next_offset(acc: int, arr: Sized) -> int: class GeoLineStringExt: """Extension for [GeoLineString][rerun.components.GeoLineString].""" + # TODO(ab): the only purpose of this override is to make the `lat_lon` arg kw-only. Should be codegen-able? + def __init__(self: Any, *, lat_lon: GeoLineStringLike): + """Create a new instance of the GeoLineString component.""" + + # You can define your own __init__ function as a member of GeoLineStringExt in geo_line_string_ext.py + self.__attrs_init__(lat_lon=lat_lon) + @staticmethod def native_to_pa_array_override(data: GeoLineStringArrayLike, data_type: pa.DataType) -> pa.Array: from ..datatypes import DVec2DBatch diff --git a/rerun_py/tests/unit/test_geo_line_strings.py b/rerun_py/tests/unit/test_geo_line_strings.py index bb8e3df89da7..bd755aeca8f9 100644 --- a/rerun_py/tests/unit/test_geo_line_strings.py +++ b/rerun_py/tests/unit/test_geo_line_strings.py @@ -75,9 +75,9 @@ def test_geo_line_strings() -> None: radii = cast(Optional[Float32ArrayLike], radii) colors = cast(Optional[Rgba32ArrayLike], colors) - print(f"rr.GeoLineStrings(\n {strips}\n radii={radii!r}\n colors={colors!r}\n)") + print(f"rr.GeoLineStrings(\n lat_lon={strips}\n radii={radii!r}\n colors={colors!r}\n)") arch = rr.GeoLineStrings( - strips, + lat_lon=strips, radii=radii, colors=colors, ) @@ -96,7 +96,7 @@ def test_geo_line_strings() -> None: ], ) def test_geo_line_strings_segment(data: GeoLineStringArrayLike) -> None: - arch = rr.GeoLineStrings(data) + arch = rr.GeoLineStrings(lat_lon=data) assert arch.line_strings == GeoLineStringBatch([ [[0, 0], [2, 1]], @@ -107,13 +107,13 @@ def test_geo_line_strings_segment(data: GeoLineStringArrayLike) -> None: def test_geo_line_strings_single_line() -> None: # Regression test for #3643 # Single line string can be passed and is not interpreted as a batch of zero-sized line strings. - reference = rr.GeoLineStrings([rr.components.GeoLineString([[0, 0], [1, 1]])]) + reference = rr.GeoLineStrings(lat_lon=[rr.components.GeoLineString(lat_lon=[[0, 0], [1, 1]])]) assert len(reference.line_strings) == 1 - assert reference == rr.GeoLineStrings(rr.components.GeoLineString([[0, 0], [1, 1]])) - assert reference == rr.GeoLineStrings([[[0, 0], [1, 1]]]) - assert reference == rr.GeoLineStrings([[0, 0], [1, 1]]) - assert reference == rr.GeoLineStrings(np.array([[0, 0], [1, 1]])) - assert reference == rr.GeoLineStrings([np.array([0, 0]), np.array([1, 1])]) + assert reference == rr.GeoLineStrings(lat_lon=rr.components.GeoLineString(lat_lon=[[0, 0], [1, 1]])) + assert reference == rr.GeoLineStrings(lat_lon=[[[0, 0], [1, 1]]]) + assert reference == rr.GeoLineStrings(lat_lon=[[0, 0], [1, 1]]) + assert reference == rr.GeoLineStrings(lat_lon=np.array([[0, 0], [1, 1]])) + assert reference == rr.GeoLineStrings(lat_lon=[np.array([0, 0]), np.array([1, 1])]) def test_geo_line_strings_invalid_shapes() -> None: @@ -122,14 +122,14 @@ def test_geo_line_strings_invalid_shapes() -> None: # We used to support flat arrays but this becomes too ambiguous when passing a single strip. with pytest.raises(ValueError): rr.GeoLineStrings( - [ + lat_lon=[ [0, 0, 2, 1, 4, -1, 6, 0], [0, 3, 1, 4, 2, 2, 3, 4, 4, 2, 5, 4, 6, 3], ], ) with pytest.raises(ValueError): rr.GeoLineStrings( - [ + lat_lon=[ np.array([0, 0, 2, 1, 4, -1, 6, 0], dtype=np.float64), np.array([0, 3, 1, 4, 2, 2, 3, 4, 4, 2, 5, 4, 6, 3], dtype=np.float64), ], @@ -138,14 +138,14 @@ def test_geo_line_strings_invalid_shapes() -> None: # not homogeneous numpy arrays with pytest.raises(ValueError): rr.GeoLineStrings( - np.array([ + lat_lon=np.array([ [[0, 0], (2, 1), [4, -1], (6, 0)], [[0, 3], (1, 4), [2, 2], (3, 4), [4, 2], (5, 4), [6, 3]], ]) ) with pytest.raises(ValueError): rr.GeoLineStrings( - np.array([ + lat_lon=np.array([ [0, 0, 2, 1, 4, -1, 6, 0], [0, 3, 1, 4, 2, 2, 3, 4, 4, 2, 5, 4, 6, 3], ]), diff --git a/rerun_py/tests/unit/test_geopoints.py b/rerun_py/tests/unit/test_geopoints.py index 95c626f290ee..9472decc3581 100644 --- a/rerun_py/tests/unit/test_geopoints.py +++ b/rerun_py/tests/unit/test_geopoints.py @@ -42,9 +42,9 @@ def test_geopoints() -> None: radii = cast(Optional[Float32ArrayLike], radii) colors = cast(Optional[Rgba32ArrayLike], colors) - print(f"rr.GeoPoints(\n {positions}\n radii={radii!r}\n colors={colors!r}\n)") + print(f"rr.GeoPoints(\n lat_lon={positions}\n radii={radii!r}\n colors={colors!r}\n)") arch = rr.GeoPoints( - positions, + lat_lon=positions, radii=radii, colors=colors, ) @@ -55,18 +55,6 @@ def test_geopoints() -> None: assert arch.colors == colors_expected(colors) -def test_geopoints_lat_lon_constructors() -> None: - positions_lat_lon = np.array([[59.319221, 18.075631], [50.319221, 12.075631]]) - positions_lon_lat = np.fliplr(positions_lat_lon) - - arch1 = rr.GeoPoints.from_lat_lon(positions_lat_lon) - arch2 = rr.GeoPoints.from_lon_lat(positions_lon_lat) - arch3 = rr.GeoPoints(positions_lat_lon) - - assert arch1.positions == arch2.positions - assert arch2.positions == arch3.positions - - @pytest.mark.parametrize( "data", [ @@ -78,7 +66,7 @@ def test_geopoints_lat_lon_constructors() -> None: ], ) def test_geopoint_single_color(data: Rgba32ArrayLike) -> None: - pts = rr.GeoPoints(np.zeros((5, 2)), colors=data) + pts = rr.GeoPoints(lat_lon=np.zeros((5, 2)), colors=data) assert pts.colors == ColorBatch(Color([0, 128, 0, 255])) @@ -104,7 +92,7 @@ def test_geopoint_single_color(data: Rgba32ArrayLike) -> None: ], ) def test_point2d_multiple_colors(data: Rgba32ArrayLike) -> None: - pts = rr.GeoPoints(np.zeros((5, 2)), colors=data) + pts = rr.GeoPoints(lat_lon=np.zeros((5, 2)), colors=data) assert pts.colors == ColorBatch([ Color([0, 128, 0, 255]), From 2c221fcac286915ccee85f4ea787d94b944c49bf Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Mon, 4 Nov 2024 16:42:24 +0100 Subject: [PATCH 20/28] Fix `GeoLineString` ui --- crates/utils/re_format/src/lib.rs | 17 +++++++++++++ .../re_component_ui/src/geo_line_string.rs | 25 +++++++++++-------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/crates/utils/re_format/src/lib.rs b/crates/utils/re_format/src/lib.rs index 393be9c5b6cc..b5e54aa61281 100644 --- a/crates/utils/re_format/src/lib.rs +++ b/crates/utils/re_format/src/lib.rs @@ -316,6 +316,23 @@ pub fn format_f32(value: f32) -> String { FloatFormatOptions::DEFAULT_f32.format(value) } +/// Format a latitude or longitude value. +/// +/// For human eyes only. +pub fn format_lat_lon(value: f64) -> String { + format!( + "{}°", + FloatFormatOptions { + always_sign: true, + precision: 10, + num_decimals: Some(6), + strip_trailing_zeros: false, + min_decimals_for_thousands_separators: 10, + } + .format_f64(value) + ) +} + #[test] fn test_format_f32() { let cases = [ diff --git a/crates/viewer/re_component_ui/src/geo_line_string.rs b/crates/viewer/re_component_ui/src/geo_line_string.rs index 42b2e4d62bdb..51e9530d5711 100644 --- a/crates/viewer/re_component_ui/src/geo_line_string.rs +++ b/crates/viewer/re_component_ui/src/geo_line_string.rs @@ -1,9 +1,8 @@ -use re_format::{format_f64, format_uint}; +use re_format::{format_lat_lon, format_uint}; use re_types::components::GeoLineString; +use re_ui::UiExt as _; use re_viewer_context::{MaybeMutRef, UiLayout, ViewerContext}; -use crate::DEFAULT_NUMBER_WIDTH; - fn singleline_view_geo_line_string( _ctx: &ViewerContext<'_>, ui: &mut egui::Ui, @@ -25,20 +24,18 @@ fn multiline_view_geo_line_string( // TODO(andreas): Editing this would be nice! let value = value.as_ref(); - // TODO(andreas): Is it really a good idea to always have the full table here? - // Can we use the ui stack to know where we are and do the right thing instead? UiLayout::SelectionPanelFull .table(ui) .resizable(true) .cell_layout(egui::Layout::left_to_right(egui::Align::Center)) - .columns(Column::initial(DEFAULT_NUMBER_WIDTH).clip(true), 2) + .columns(Column::initial(100.0).clip(true), 2) .header(re_ui::DesignTokens::table_header_height(), |mut header| { re_ui::DesignTokens::setup_table_header(&mut header); header.col(|ui| { - ui.label("latitude"); + ui.label("Latitude"); }); header.col(|ui| { - ui.label("longitude"); + ui.label("Longitude"); }); }) .body(|mut body| { @@ -47,10 +44,18 @@ fn multiline_view_geo_line_string( body.rows(row_height, value.0.len(), |mut row| { if let Some(pos) = value.0.get(row.index()) { row.col(|ui| { - ui.label(format_f64(pos.x())); + ui.label(format_lat_lon(pos.x())).on_hover_ui(|ui| { + ui.markdown_ui( + "Latitude according to [EPSG:4326](https://epsg.io/4326)", + ); + }); }); row.col(|ui| { - ui.label(format_f64(pos.y())); + ui.label(format_lat_lon(pos.y())).on_hover_ui(|ui| { + ui.markdown_ui( + "Longitude according to [EPSG:4326](https://epsg.io/4326)", + ); + }); }); } }); From 778c47c0d1ecc9b11b93480598ec7d48de4c18f1 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Mon, 4 Nov 2024 16:50:13 +0100 Subject: [PATCH 21/28] Custom ui for `LatLon` --- crates/viewer/re_component_ui/src/lat_lon.rs | 24 ++++++++++++++++++++ crates/viewer/re_component_ui/src/lib.rs | 3 +++ 2 files changed, 27 insertions(+) create mode 100644 crates/viewer/re_component_ui/src/lat_lon.rs diff --git a/crates/viewer/re_component_ui/src/lat_lon.rs b/crates/viewer/re_component_ui/src/lat_lon.rs new file mode 100644 index 000000000000..96beff14c832 --- /dev/null +++ b/crates/viewer/re_component_ui/src/lat_lon.rs @@ -0,0 +1,24 @@ +use re_format::format_lat_lon; +use re_types::components::LatLon; +use re_ui::UiExt as _; +use re_viewer_context::{MaybeMutRef, UiLayout, ViewerContext}; + +pub fn singleline_view_lat_lon( + _ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + value: &mut MaybeMutRef<'_, LatLon>, +) -> egui::Response { + let value = value.as_ref(); + UiLayout::List + .label( + ui, + format!( + "{}, {}", + format_lat_lon(value.latitude()), + format_lat_lon(value.longitude()) + ), + ) + .on_hover_ui(|ui| { + ui.markdown_ui("Latitude and longitude according to [EPSG:4326](https://epsg.io/4326)"); + }) +} diff --git a/crates/viewer/re_component_ui/src/lib.rs b/crates/viewer/re_component_ui/src/lib.rs index 6c7d4c8b0c2a..d66f1c791276 100644 --- a/crates/viewer/re_component_ui/src/lib.rs +++ b/crates/viewer/re_component_ui/src/lib.rs @@ -9,6 +9,7 @@ mod entity_path; mod fallback_ui; mod geo_line_string; mod image_format; +mod lat_lon; mod line_strip; mod map_provider; mod marker_shape; @@ -163,5 +164,7 @@ pub fn create_component_ui_registry() -> re_viewer_context::ComponentUiRegistry registry.add_singleline_edit_or_view(video_timestamp::edit_or_view_timestamp); + registry.add_singleline_edit_or_view(lat_lon::singleline_view_lat_lon); + registry } From cc0e527a1a8d5205d2ecf825295bcaaae2001429 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler <49431240+abey79@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:41:55 +0100 Subject: [PATCH 22/28] Update rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp Co-authored-by: Emil Ernerfeldt --- rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp b/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp index 25c9eeebff52..1d2ad55e4a7e 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp +++ b/rerun_cpp/src/rerun/archetypes/geo_points_ext.cpp @@ -8,7 +8,7 @@ namespace rerun { #ifdef EDIT_EXTENSION // - /// Creates a new GeoPoints object based on EPSG:4326 latitude and longitude (North/East-positive degrees). + /// Creates a new GeoPoints object based on [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). static GeoPoints from_lat_lon(Collection positions_) { GeoPoints points; points.positions = std::move(positions_); From b262a0029239823867b76ca705ea98d0519db4a1 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler <49431240+abey79@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:42:05 +0100 Subject: [PATCH 23/28] Update rerun_cpp/src/rerun/components/geo_line_string_ext.cpp Co-authored-by: Emil Ernerfeldt --- rerun_cpp/src/rerun/components/geo_line_string_ext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp b/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp index 469c7a8e45bb..483343f57f04 100644 --- a/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp +++ b/rerun_cpp/src/rerun/components/geo_line_string_ext.cpp @@ -8,7 +8,7 @@ namespace rerun { #ifdef EDIT_EXTENSION // - /// Creates a new GeoLineString object based on EPSG:4326 latitude and longitude (North/East-positive degrees). + /// Creates a new GeoLineString object based on [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). static GeoLineString from_lat_lon(Collection lat_lon_) { GeoLineString line_string; line_string.lat_lon = std::move(lat_lon_); From 1925d6d773225015cd81f51493fdc211a9ed34f8 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Mon, 4 Nov 2024 18:00:19 +0100 Subject: [PATCH 24/28] codegen --- rerun_cpp/src/rerun/archetypes/geo_points.hpp | 2 +- .../src/rerun/components/geo_line_string.hpp | 2 +- .../rerun/archetypes/geo_line_strings.py | 26 +------------------ .../rerun_sdk/rerun/archetypes/geo_points.py | 4 +-- 4 files changed, 5 insertions(+), 29 deletions(-) diff --git a/rerun_cpp/src/rerun/archetypes/geo_points.hpp b/rerun_cpp/src/rerun/archetypes/geo_points.hpp index 14d06e2579a5..8f3ad38b0e3f 100644 --- a/rerun_cpp/src/rerun/archetypes/geo_points.hpp +++ b/rerun_cpp/src/rerun/archetypes/geo_points.hpp @@ -60,7 +60,7 @@ namespace rerun::archetypes { using IndicatorComponent = rerun::components::IndicatorComponent; public: // START of extensions from geo_points_ext.cpp: - /// Creates a new GeoPoints object based on EPSG:4326 latitude and longitude (North/East-positive degrees). + /// Creates a new GeoPoints object based on [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). static GeoPoints from_lat_lon(Collection positions_) { GeoPoints points; points.positions = std::move(positions_); diff --git a/rerun_cpp/src/rerun/components/geo_line_string.hpp b/rerun_cpp/src/rerun/components/geo_line_string.hpp index d7d3a61a0a6b..73aefb331096 100644 --- a/rerun_cpp/src/rerun/components/geo_line_string.hpp +++ b/rerun_cpp/src/rerun/components/geo_line_string.hpp @@ -22,7 +22,7 @@ namespace rerun::components { rerun::Collection lat_lon; public: // START of extensions from geo_line_string_ext.cpp: - /// Creates a new GeoLineString object based on EPSG:4326 latitude and longitude (North/East-positive degrees). + /// Creates a new GeoLineString object based on [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). static GeoLineString from_lat_lon(Collection lat_lon_) { GeoLineString line_string; line_string.lat_lon = std::move(lat_lon_); diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py index dfc2b51b66fe..c0791ccf7acd 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_line_strings.py @@ -36,7 +36,7 @@ class GeoLineStrings(GeoLineStringsExt, Archetype): rr.log( "colorado", rr.GeoLineStrings( - [ + lat_lon=[ [41.0000, -109.0452], [41.0000, -102.0415], [36.9931, -102.0415], @@ -47,30 +47,6 @@ class GeoLineStrings(GeoLineStringsExt, Archetype): colors=[0, 0, 255], ), ) - - rr.log( - "colorado", - rr.GeoLineStrings( - lat_lon=[ - [ - [41.0000, -109.0452], - [41.0000, -102.0415], - [36.9931, -102.0415], - [36.9931, -109.0452], - [41.0000, -109.0452], - ], - rr.components.GeoLineString( - lat_lon=[ - [41.0000, -109.0452], - [36.9931, -109.0452], - [41.0000, -109.0452], - ] - ), - ], - radii=rr.Radius.ui_points(2.0), - colors=[0, 0, 255], - ), - ) ``` """ diff --git a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py index b8972a64705b..c91b86e47f3e 100644 --- a/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py +++ b/rerun_py/rerun_sdk/rerun/archetypes/geo_points.py @@ -33,8 +33,8 @@ class GeoPoints(GeoPointsExt, Archetype): rr.log( "rerun_hq", - rr.GeoPoints.from_lat_lon( - [59.319221, 18.075631], + rr.GeoPoints( + lat_lon=[59.319221, 18.075631], radii=rr.Radius.ui_points(10.0), colors=[255, 0, 0], ), From 314e3f106fb95b1c67f8923e6588055b9c43660e Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Wed, 6 Nov 2024 11:13:35 +0100 Subject: [PATCH 25/28] codegen --- docs/content/reference/types/components/geo_line_string.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/content/reference/types/components/geo_line_string.md b/docs/content/reference/types/components/geo_line_string.md index 89fab8466b5d..f03f0f9259c9 100644 --- a/docs/content/reference/types/components/geo_line_string.md +++ b/docs/content/reference/types/components/geo_line_string.md @@ -5,9 +5,11 @@ title: "GeoLineString" A geospatial line string expressed in [EPSG:4326](https://epsg.io/4326) latitude and longitude (North/East-positive degrees). -## Fields -* lat_lon: list of [`DVec2D`](../datatypes/dvec2d.md) +## Arrow datatype +``` +List> +``` ## API reference links * 🌊 [C++ API docs for `GeoLineString`](https://ref.rerun.io/docs/cpp/stable/structrerun_1_1components_1_1GeoLineString.html?speculative-link) From 63425de5961408249a1751a8b95dfbeb0eec771d Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Wed, 6 Nov 2024 15:23:15 +0100 Subject: [PATCH 26/28] Fix map view example snippet --- docs/snippets/all/views/map.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/snippets/all/views/map.py b/docs/snippets/all/views/map.py index 64fa46a4f48c..4dc27e70099a 100644 --- a/docs/snippets/all/views/map.py +++ b/docs/snippets/all/views/map.py @@ -5,7 +5,7 @@ rr.init("rerun_example_map_view", spawn=True) -rr.log("points", rr.GeoPoints([[47.6344, 19.1397], [47.6334, 19.1399]])) +rr.log("points", rr.GeoPoints(lat_lon=[[47.6344, 19.1397], [47.6334, 19.1399]])) # Create a map view to display the chart. # TODO(#7903): cleanup the blueprint API for the map view From 6e162e9db86fe24cf7ccf3a1da55bb41b11740e7 Mon Sep 17 00:00:00 2001 From: Antoine Beyeler Date: Wed, 6 Nov 2024 18:43:26 +0100 Subject: [PATCH 27/28] Codegen, again --- rerun_py/rerun_sdk/rerun/blueprint/views/map_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rerun_py/rerun_sdk/rerun/blueprint/views/map_view.py b/rerun_py/rerun_sdk/rerun/blueprint/views/map_view.py index 19ec551472d6..638fcc89c9ea 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/views/map_view.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/views/map_view.py @@ -28,7 +28,7 @@ class MapView(SpaceView): rr.init("rerun_example_map_view", spawn=True) - rr.log("points", rr.GeoPoints([[47.6344, 19.1397], [47.6334, 19.1399]])) + rr.log("points", rr.GeoPoints(lat_lon=[[47.6344, 19.1397], [47.6334, 19.1399]])) # Create a map view to display the chart. # TODO(#7903): cleanup the blueprint API for the map view From eb90eb42a8a90ae32064d8b33573715253d7d413 Mon Sep 17 00:00:00 2001 From: Clement Rey Date: Thu, 7 Nov 2024 15:02:50 +0100 Subject: [PATCH 28/28] do not ever drain ALL_RECORDINGS --- rerun_py/src/python_bridge.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/rerun_py/src/python_bridge.rs b/rerun_py/src/python_bridge.rs index 36a229af51b7..2d2e561e1429 100644 --- a/rerun_py/src/python_bridge.rs +++ b/rerun_py/src/python_bridge.rs @@ -314,9 +314,21 @@ fn shutdown(py: Python<'_>) { re_log::debug!("Shutting down the Rerun SDK"); // Release the GIL in case any flushing behavior needs to cleanup a python object. py.allow_threads(|| { - for (_, recording) in all_recordings().drain() { + // NOTE: Do **NOT** try and drain() `all_recordings` here. + // + // Doing so would drop the last remaining reference to these recordings, and therefore + // trigger their deallocation as well as the deallocation of all the Python and C++ data + // that they might transitively reference, but this is _NOT_ the right place to do so. + // This method is called automatically during shutdown via python's `atexit`, which is not + // a safepoint for deallocating these things, quite far from it. + // + // Calling `disconnect()` will already take care of flushing everything that can be flushed, + // and cleaning up everything that can be safely cleaned up, anyhow. + // Whatever's left can wait for the OS to clean it up. + for (_, recording) in all_recordings().iter() { recording.disconnect(); } + flush_garbage_queue(); }); }