Repository navigation
feat(map): QGIS import colours rasters as QGIS does, and QGIS raster styles apply to rasters #877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
80208e2
bea9dd1
9b05958
b375ca3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
|
|
||
| mod expr; | ||
| mod labels; | ||
| mod raster; | ||
| mod renderer; | ||
| mod source; | ||
| mod symbol; | ||
|
|
@@ -108,6 +109,8 @@ pub struct Style { | |
| /// 0 opaque to 100 invisible. | ||
| pub transparency: Option<f32>, | ||
| pub blend: Option<Blend>, | ||
| /// A raster's colouring, stretch, resampling, brightness, contrast and mask. | ||
| pub raster: Option<crate::raster::RasterStyle>, | ||
| /// Drawn between these scales (RustGIS's, see [`QGIS_SCALE`]): zoomed in beyond 1:`min_scale` | ||
| /// and out beyond 1:`max_scale`. `Some(None)` clears a limit. | ||
| pub min_scale: Option<Option<f64>>, | ||
|
|
@@ -117,6 +120,26 @@ pub struct Style { | |
| impl Style { | ||
| /// Gives `layer` this style's parts (those it sets). | ||
| pub fn apply(&self, layer: &mut crate::Layer) { | ||
| if let Some(t) = self.transparency { | ||
| layer.transparency = t; | ||
| } | ||
| if let Some(b) = self.blend { | ||
| layer.blend = b; | ||
| } | ||
| if let Some(s) = self.min_scale { | ||
| layer.min_scale = s; | ||
| } | ||
| if let Some(s) = self.max_scale { | ||
| layer.max_scale = s; | ||
| } | ||
| // A raster takes its raster style (when its bands are there), and none of a feature | ||
| // layer's parts. | ||
| if let Some(r) = layer.raster.as_mut() { | ||
| if let Some(st) = self.raster.as_ref().filter(|_| self.raster_misfit(r).is_none()) { | ||
| r.style = st.clone(); | ||
| } | ||
| return; | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| if let Some(r) = &self.renderer { | ||
| layer.renderer = r.clone(); | ||
| } | ||
|
|
@@ -132,18 +155,15 @@ impl Style { | |
| if let Some(l) = &self.labels { | ||
| layer.labels = l.clone(); | ||
| } | ||
| if let Some(t) = self.transparency { | ||
| layer.transparency = t; | ||
| } | ||
| if let Some(b) = self.blend { | ||
| layer.blend = b; | ||
| } | ||
| if let Some(s) = self.min_scale { | ||
| layer.min_scale = s; | ||
| } | ||
| if let Some(s) = self.max_scale { | ||
| layer.max_scale = s; | ||
| } | ||
| } | ||
|
|
||
| /// Why this style's raster colouring doesn't fit the raster `r` (it reads a band the file | ||
| /// hasn't), or `None` when it fits or there is none. | ||
| pub fn raster_misfit(&self, r: &crate::raster::RasterLayer) -> Option<String> { | ||
| let st = self.raster.as_ref()?; | ||
| let count = r.data.as_ref()?.band_count(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Low-confidence:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leaving it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Follow-up: CodeRabbit raised the same case, and 090813b now notes it at style import (applied, bands not yet checked) instead of passing silently. The band count still isn't known before the pixels land, so the style is still applied, and |
||
| let band = st.renderer.bands().into_iter().find(|b| *b >= count)?; | ||
| Some(format!("its raster colouring, which reads band {} of a raster with {count}", band + 1)) | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -305,12 +325,12 @@ fn read_style_node(node: Node, layer: &str, report: &mut Report) -> Style { | |
| if let Some(r) = child(node, "renderer-v2") { | ||
| renderer::read(r, layer, &mut style, report); | ||
| } | ||
| // A raster's renderer: its opacity carried over, its colouring not yet. | ||
| // A raster's renderer: its opacity, and its colouring as RustGIS's raster style. | ||
| if let Some(r) = child(node, "pipe").and_then(|p| child(p, "rasterrenderer")) { | ||
| if let Some(o) = r.attribute("opacity").and_then(|o| o.trim().parse::<f32>().ok()).filter(|o| o.is_finite() && *o < 1.0) { | ||
| style.transparency = Some(((1.0 - o.clamp(0.0, 1.0)) * 100.0).round()); | ||
| } | ||
| report.skipped(layer, format!("its raster colouring ({} renderer)", r.attribute("type").unwrap_or("unknown"))); | ||
| style.raster = raster::read(node, layer, report); | ||
| } | ||
| if let Some(l) = child(node, "labeling") { | ||
| // Set up but switched off: carried over, switched off. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent with
import_style: here, when the raster colouring doesn't fit,layer.style.apply(l)still applies opacity, blend and scale range (only the raster style is skipped), whereasimport_styleleaves the raster entirely as it was ("its opacity and scale range too"). The note says only the colouring is dropped, so for a project import this is accurate, but the two paths behave differently for the same style. Also, in the project import path, a raster whose data is still loading (data == None) gets no "unchecked" note, unlikeimport_style. Confidence: medium-low (consistency/UX, not a crash).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving this on purpose. The two paths do different things. A project import adds the layer from the project, so opacity, blending and scale range are that layer's own settings and come in whatever happens to its colouring. Only the colouring is noted as skipped.
style.importQgisrestyles a layer that already looks the way the user set it, and a style that doesn't fit leaves it as it was, as the docs say. As for the loading case in project import: a project's rasters are added from their paths there, and a remote one that is still loading is clamped byrepairwhen it lands. Noting every remote raster of a project as "unchecked" would add a note per layer for what is normally fine.