Skip to content

Commit

Permalink
feat(worker): add CityGmlGeometryLodFilter processor for geometry (#501)
Browse files Browse the repository at this point in the history
* feat(worker): add CityGmlGeometryLodFilter processor for geometry

* feat(worker): enhance CityGmlGeometryLodFilter to include rejected port and update description

* fix
  • Loading branch information
miseyu authored Sep 17, 2024
1 parent b4ba821 commit e9c373f
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 2 deletions.
34 changes: 34 additions & 0 deletions schema/actions.json
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,40 @@
"File"
]
},
{
"name": "CityGmlGeometryLodFilter",
"type": "processor",
"description": "Filters CityGML geometries by LOD",
"parameter": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "CityGmlGeometryLodFilter",
"type": "object",
"required": [
"lods"
],
"properties": {
"lods": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
}
}
},
"builtin": true,
"inputPorts": [
"default"
],
"outputPorts": [
"default",
"rejected"
],
"categories": [
"Geometry"
]
},
{
"name": "Clipper",
"type": "processor",
Expand Down
1 change: 1 addition & 0 deletions worker/crates/action-processor/src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod area_on_area_overlayer;
pub mod bounds_extractor;
pub mod bufferer;
pub mod center_point_replacer;
pub mod city_gml_geometry_lod_filter;
pub mod clipper;
pub mod closed_curve_filter;
pub mod coercer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use std::collections::HashMap;

use reearth_flow_runtime::{
channels::ProcessorChannelForwarder,
errors::BoxedError,
event::EventHub,
executor_operation::{ExecutorContext, NodeContext},
node::{Port, Processor, ProcessorFactory, DEFAULT_PORT, REJECTED_PORT},
};
use reearth_flow_types::GeometryValue;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use super::errors::GeometryProcessorError;

#[derive(Debug, Clone, Default)]
pub struct CityGmlGeometryLodFilterFactory;

impl ProcessorFactory for CityGmlGeometryLodFilterFactory {
fn name(&self) -> &str {
"CityGmlGeometryLodFilter"
}

fn description(&self) -> &str {
"Filters CityGML geometries by LOD"
}

fn parameter_schema(&self) -> Option<schemars::schema::RootSchema> {
Some(schemars::schema_for!(CityGmlGeometryLodFilter))
}

fn categories(&self) -> &[&'static str] {
&["Geometry"]
}

fn get_input_ports(&self) -> Vec<Port> {
vec![DEFAULT_PORT.clone()]
}

fn get_output_ports(&self) -> Vec<Port> {
vec![DEFAULT_PORT.clone(), REJECTED_PORT.clone()]
}
fn build(
&self,
_ctx: NodeContext,
_event_hub: EventHub,
_action: String,
with: Option<HashMap<String, Value>>,
) -> Result<Box<dyn Processor>, BoxedError> {
let filter: CityGmlGeometryLodFilter = if let Some(with) = with {
let value: Value = serde_json::to_value(with).map_err(|e| {
GeometryProcessorError::CityGmlGeometryLodFilterFactory(format!(
"Failed to serialize `with` parameter: {}",
e
))
})?;
serde_json::from_value(value).map_err(|e| {
GeometryProcessorError::CityGmlGeometryLodFilterFactory(format!(
"Failed to deserialize `with` parameter: {}",
e
))
})?
} else {
return Err(GeometryProcessorError::CityGmlGeometryLodFilterFactory(
"Missing required parameter `with`".to_string(),
)
.into());
};
if filter.lods.iter().any(|lod| *lod > 4) {
return Err(GeometryProcessorError::CityGmlGeometryLodFilterFactory(
"LOD must be between 0 and 4".to_string(),
)
.into());
}
Ok(Box::new(filter))
}
}

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CityGmlGeometryLodFilter {
lods: Vec<u8>,
}

impl Processor for CityGmlGeometryLodFilter {
fn initialize(&mut self, _ctx: NodeContext) {}

fn num_threads(&self) -> usize {
5
}

fn process(
&mut self,
ctx: ExecutorContext,
fw: &mut dyn ProcessorChannelForwarder,
) -> Result<(), BoxedError> {
let feature = &ctx.feature;
if let Some(geometry) = &feature.geometry {
match &geometry.value {
GeometryValue::CityGmlGeometry(v) => {
let mut feature = feature.clone();
let mut geometry = geometry.clone();
let mut geometry_value = v.clone();
geometry_value.gml_geometries.retain(|g| {
if let Some(lod) = g.lod {
self.lods.contains(&lod)
} else {
false
}
});
if geometry_value.gml_geometries.is_empty() {
fw.send(ctx.new_with_feature_and_port(feature, REJECTED_PORT.clone()));
return Ok(());
}
geometry.value = GeometryValue::CityGmlGeometry(geometry_value);
feature.geometry = Some(geometry);
fw.send(ctx.new_with_feature_and_port(feature, DEFAULT_PORT.clone()));
}
GeometryValue::FlowGeometry2D(_) => {
return Err(GeometryProcessorError::CityGmlGeometryLodFilter(
"FlowGeometry2D is not supported".to_string(),
)
.into());
}
GeometryValue::FlowGeometry3D(_) => {
return Err(GeometryProcessorError::CityGmlGeometryLodFilter(
"FlowGeometry3D is not supported".to_string(),
)
.into());
}
GeometryValue::None => {
return Err(GeometryProcessorError::CityGmlGeometryLodFilter(
"GeometryValue is None".to_string(),
)
.into());
}
}
}
Ok(())
}

fn finish(
&self,
_ctx: NodeContext,
_fw: &mut dyn ProcessorChannelForwarder,
) -> Result<(), BoxedError> {
Ok(())
}

fn name(&self) -> &str {
"CityGmlGeometryLodFilter"
}
}
4 changes: 4 additions & 0 deletions worker/crates/action-processor/src/geometry/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ pub(super) enum GeometryProcessorError {
DimensionFilterFactory(String),
#[error("DimensionFilter error: {0}")]
DimensionFilter(String),
#[error("CityGmlGeometryLodFilter Factory error: {0}")]
CityGmlGeometryLodFilterFactory(String),
#[error("CityGmlGeometryLodFilter error: {0}")]
CityGmlGeometryLodFilter(String),
}

pub(super) type Result<T, E = GeometryProcessorError> = std::result::Result<T, E>;
6 changes: 4 additions & 2 deletions worker/crates/action-processor/src/geometry/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use reearth_flow_runtime::node::{NodeKind, ProcessorFactory};
use super::{
area_on_area_overlayer::AreaOnAreaOverlayerFactory, bounds_extractor::BoundsExtractorFactory,
bufferer::BuffererFactory, center_point_replacer::CenterPointReplacerFactory,
clipper::ClipperFactory, closed_curve_filter::ClosedCurveFilterFactory,
coercer::GeometryCoercerFactory, coordinate_system_setter::CoordinateSystemSetterFactory,
city_gml_geometry_lod_filter::CityGmlGeometryLodFilterFactory, clipper::ClipperFactory,
closed_curve_filter::ClosedCurveFilterFactory, coercer::GeometryCoercerFactory,
coordinate_system_setter::CoordinateSystemSetterFactory,
dimension_filter::DimensionFilterFactory, dissolver::GeometryDissolverFactory,
elevation_extractor::ElevationExtractorFactory, extractor::GeometryExtractorFactory,
extruder::ExtruderFactory, filter::GeometryFilterFactory, hole_counter::HoleCounterFactory,
Expand Down Expand Up @@ -53,6 +54,7 @@ pub static ACTION_FACTORY_MAPPINGS: Lazy<HashMap<String, NodeKind>> = Lazy::new(
Box::<ElevationExtractorFactory>::default(),
Box::<GeometryDissolverFactory>::default(),
Box::<DimensionFilterFactory>::default(),
Box::<CityGmlGeometryLodFilterFactory>::default(),
];
factories
.into_iter()
Expand Down
34 changes: 34 additions & 0 deletions worker/docs/mdbook/src/action.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,40 @@ Writes features to a file
### Category
* File

## CityGmlGeometryLodFilter
### Type
* processor
### Description
Filters CityGML geometries by LOD
### Parameters
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "CityGmlGeometryLodFilter",
"type": "object",
"required": [
"lods"
],
"properties": {
"lods": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
}
}
}
```
### Input Ports
* default
### Output Ports
* default
* rejected
### Category
* Geometry

## Clipper
### Type
* processor
Expand Down

0 comments on commit e9c373f

Please sign in to comment.