Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions TODO_AFTER_LAYER_REFACTORING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- remove `helpers`
- remove `voxel-helpers`
- re-add zoom to object on permalink (see behavior in old sidebar)
- reorder pick results by order of layers
- add 3dtiles v1.1 code to refactored layers (+ attempt to use shader instead of alpha color)
18 changes: 5 additions & 13 deletions api/src/layers/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ pub struct LayerConfig {
#[serde(default, skip_serializing)]
pub voxel_mappings: HashMap<String, VoxelMappingDefinition>,

/// A list of voxel filters that may be reused by multiple layers.
/// Each entry's key is used to identify it within this config.
#[serde(default, skip_serializing)]
pub voxel_filters: HashMap<String, VoxelFilterDefinition>,

/// A list of voxel band displays that may be reused by multiple layers.
/// Each entry's key is used to identify it within this config.
#[serde(default, skip_serializing)]
Expand Down Expand Up @@ -133,7 +128,6 @@ impl LayerConfig {
layers: vec![],
groups: vec![],
voxel_mappings: std::mem::take(&mut self.voxel_mappings),
voxel_filters: std::mem::take(&mut self.voxel_filters),
tiff_displays: Default::default(),
};

Expand Down Expand Up @@ -175,17 +169,15 @@ impl LayerConfig {
}

for (key, mapping) in &context.config.voxel_mappings {
if mapping.use_count == 0 {
let use_count = match mapping {
VoxelMappingDefinition::Range(it) => it.use_count,
VoxelMappingDefinition::Category(it) => it.use_count,
};
if use_count == 0 {
tracing::warn!("[{}] Voxel mapping \"{key}\" is unused.", context.display)
}
}

for (key, filter) in &context.config.voxel_filters {
if filter.use_count == 0 {
tracing::warn!("[{}] Voxel filter \"{key}\" is unused.", context.display)
}
}

for (key, group) in context.known_groups {
if group.use_count == 0 {
tracing::warn!("[{}] Group \"{key}\" is unused.", context.display)
Expand Down
160 changes: 79 additions & 81 deletions api/src/layers/voxel.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,87 @@
use crate::layers::config::{Parse, ParseContext};
use anyhow::anyhow;
use serde::{Deserialize, Deserializer, Serialize};
use crate::LayerSource;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all(serialize = "camelCase"))]
pub struct VoxelLayer {
pub url: String,

/// The translation key providing the display name for the unit of the layer's values.
// TODO document what happens when this is not set.
pub unit_label: Option<String>,
pub source: LayerSource,

/// The key of the property that contains the layer's data points.
/// Note that there will need to be a mapping for this key for the layer to render.
pub data_key: String,

/// The value that represents the absence of data on this layer.
pub no_data: i32,
pub values: VoxelLayerValues,

/// The layer's value mappings.
/// This determines how the layer can be rendered and otherwise displayed to the user.
pub mappings: Vec<VoxelLayerMapping>,
}

/// The layer's value mapping.
/// This determines how the layer is rendered and otherwise displayed to the user.
pub mapping: VoxelMapping,
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all(serialize = "camelCase"))]
pub struct VoxelLayerValues {
/// The value that represents an absent datapoint.
/// "Absence" here means that it doesn't exist, and does not need to be displayed.
pub no_data: i32,

/// The layer's value filter configuration.
/// This determines how the user is able to filter the layer's data points.
pub filter: VoxelFilter,
/// The value that represents a datapoint without a value.
/// The datapoint still exists and should be displayed, it just isn't backed by a meaningful value.
///
/// The main use of undefined values are datapoints that are only meaningful for specific mappings.
/// If a datapoint is undefined on all mapped keys, it may be treated as `no_data`.
pub undefined: i32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum VoxelMapping {
pub enum VoxelLayerMapping {
Reference(String),
Definition(VoxelMappingDefinition),
}

impl Parse for VoxelLayer {
fn parse(mut self, context: &mut ParseContext) -> anyhow::Result<Self> {
for mapping in &mut self.mappings {
if let VoxelLayerMapping::Reference(name) = mapping {
let definition = context
.config
.voxel_mappings
.get_mut(name)
.ok_or_else(|| anyhow!("Unknown voxel mapping: {name}"))?;
match definition {
VoxelMappingDefinition::Range(it) => it.use_count += 1,
VoxelMappingDefinition::Category(it) => it.use_count += 1,
}
*mapping = VoxelLayerMapping::Definition(definition.clone());
}
}
Ok(self)
}
}


#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum VoxelMappingDefinition {
Range(VoxelRangeMapping),
Category(VoxelItemMapping),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all(serialize = "camelCase"))]
pub struct VoxelMappingDefinition {
/// The minimum and maximum values of the layer's data points.
pub struct VoxelRangeMapping {
/// The key of the property that contains the data points.
pub key: String,

/// The minimum and maximum values of the data points.
pub range: (i32, i32),

/// The sequence of colors applied to the range of values.
/// These will be scaled linearly to fit the value range.
/// The colors with which the range is displayed.
///
/// If this has the same length as [range], each value gets its own, specific value.
/// If there are fewer colors than values, the colors are interpreted as a gradient on which the values can be placed.
pub colors: Vec<String>,

/// The number of times this definition has been referenced.
Expand All @@ -52,94 +93,51 @@ pub struct VoxelMappingDefinition {
pub use_count: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum VoxelFilter {
Reference(String),
Definition(VoxelFilterDefinition),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all(serialize = "camelCase"))]
pub struct VoxelFilterDefinition {
#[serde(default)]
pub lithology: Option<LithologyVoxelFilter>,
pub struct VoxelItemMapping {
/// The key of the property that contains the data points.
pub key: String,

#[serde(default)]
pub conductivity: Option<ConductivityVoxelFilter>,
/// The mapping's items.
/// Each item represents a unique value.
pub items: Vec<VoxelItemMappingItem>,

/// The number of times this definition has been referenced.
/// This is used to ensure that the definition is not unused.
///
/// If this definition is directly attached to a layer,
/// this field will never be used.
/// this field will never be used.
#[serde(skip, default)]
pub use_count: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all(serialize = "camelCase"))]
pub struct LithologyVoxelFilter {
/// The key of the property that contains the lithology data points.
pub key: String,

/// The filter's items.
/// Each item represents a value that can be filtered by.
pub items: Vec<LithologyVoxelFilterItem>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(deny_unknown_fields, rename_all(serialize = "camelCase"))]
pub struct LithologyVoxelFilterItem {
pub struct VoxelItemMappingItem {
/// The translation key providing the display name for the item.
pub label: String,

/// The value that the data points matching this item have.
pub value: i32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all(serialize = "camelCase"))]
pub struct ConductivityVoxelFilter {
/// The key of the property that contains the conductivity data points.
pub key: String,

/// The minimum and maximum values of the conductivity data points.
pub range: (i32, i32),
/// The color in which this value is displayed.
pub color: String,
}

/// Custom Deserialize implementation for [LithologyVoxelFilterItem] that allows
/// the configuration to be written as tuple `(u32, String)`.
impl<'de> Deserialize<'de> for LithologyVoxelFilterItem {
/// Custom Deserialize implementation for [VoxelItemMappingItem] that allows
/// the configuration to be written as tuple `(i32, { label: String, color: String })`.
impl<'de> Deserialize<'de> for VoxelItemMappingItem {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let (value, label) = <(i32, String)>::deserialize(d)?;
Ok(Self { label, value })
}
}

impl Parse for VoxelLayer {
fn parse(mut self, context: &mut ParseContext) -> anyhow::Result<Self> {
if let VoxelMapping::Reference(name) = &self.mapping {
let mapping = context
.config
.voxel_mappings
.get_mut(name)
.ok_or_else(|| anyhow!("Unknown voxel mapping: {name}"))?;
mapping.use_count += 1;
self.mapping = VoxelMapping::Definition(mapping.clone());
#[derive(Deserialize)]
struct Item {
pub label: String,
pub color: String,
}
if let VoxelFilter::Reference(name) = &self.filter {
let filter = context
.config
.voxel_filters
.get_mut(name)
.ok_or_else(|| anyhow!("Unknown voxel filter: {name}"))?;
filter.use_count += 1;
self.filter = VoxelFilter::Definition(filter.clone());
}
Ok(self)
let (value, item) = <(i32, Item)>::deserialize(d)?;
Ok(Self { label: item.label, value, color: item.color })
}
}
}
3 changes: 2 additions & 1 deletion layers/01-maps_and_models.json5
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@
{
id: 'lyr_unconsolidated_rocks_label',
children: [
'voxel_aaretal_litho'
'voxel_aaretal_lithology',
'voxel_aaretal_conductivity',
]
},

Expand Down
Loading
Loading