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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ bytemuck = { version = "1", features = ["derive"] }
lyon = "1"
image = { version = "0.25", default-features = false, features = ["png"] }
ab_glyph = "0.2"
# A label curved along a line: its text cut into the characters drawn as one (a letter with its
# accents).
unicode-segmentation = "1.13"
# Rasters: GeoTIFF decoding/encoding, and coordinate systems by EPSG code (pure Rust, except the
# Zstandard decoder `rustgis-data` turns on, C that clang compiles for the browser build too).
tiff = "0.11"
Expand Down
8 changes: 4 additions & 4 deletions apps/rustgis-python/THIRD-PARTY-LICENSES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ semver, seq-macro, serde, serde_core, serde_derive, serde_json, sha1, sha2, shle
simdutf8, siphasher, smallvec, socket2, spade, spki, stable_deref_trait, syn, sys-locale, tar,
tempfile, thiserror, thiserror-impl, tiberius-macros, time, time-core, tinyvec, tokio-postgres,
ttf-parser, typed-path, unicode-bidi, unicode-ident, unicode-normalization, unicode-properties,
ureq, ureq-proto, url, utf8-zero, utf8_iter, uuid, vcpkg, version_check, wasm-bindgen,
wasm-bindgen-macro, wasm-bindgen-macro-support, wasm-bindgen-shared, wasmi, wasmi_collections,
wasmi_core, wasmi_ir, wasmparser, web-sys, weezl, whoami, winauth, wkt, x509-cert, xattr, zeroize,
zune-core, zune-jpeg
unicode-segmentation, ureq, ureq-proto, url, utf8-zero, utf8_iter, uuid, vcpkg, version_check,
wasm-bindgen, wasm-bindgen-macro, wasm-bindgen-macro-support, wasm-bindgen-shared, wasmi,
wasmi_collections, wasmi_core, wasmi_ir, wasmparser, web-sys, weezl, whoami, winauth, wkt,
x509-cert, xattr, zeroize, zune-core, zune-jpeg
====================================================================================================

Apache License
Expand Down
2 changes: 1 addition & 1 deletion crates/engine/src/cmd/labels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub fn commands() -> Vec<C> {
})
.when(need_feature_layer)
.params(
r#"{"layer"?, "class"?: name (none: the Default class, by whatever name it has), "position": "auto"|"center"|"above"|"below"|"left"|"right"|"aboveRight"|"aboveLeft"|"belowRight"|"belowLeft"}"#,
r#"{"layer"?, "class"?: name (none: the Default class, by whatever name it has), "position": "auto"|"center"|"above"|"below"|"left"|"right"|"aboveRight"|"aboveLeft"|"belowRight"|"belowLeft"|"parallel" (along a line, straight)|"curved" (along a line, following its bends)}"#,
),
// Label classes: each labels every feature its clause takes, as in ArcGIS Pro; the classes
// listed first, Default last, are placed first (`Labeling`, #732).
Expand Down
7 changes: 5 additions & 2 deletions crates/engine/src/cmd/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ pub fn commands() -> Vec<C> {
C::new("labels.style", "Text Symbol", "Style › Text Symbol", |s, p| {
let size = f(p, "size").map(|v| v.clamp(*LABEL_SIZE.start(), *LABEL_SIZE.end()) as f32);
let color = s.get_color(p, "color")?;
let (halo, bold) = (b(p, "halo"), b(p, "bold"));
let (halo, bold, italic) = (b(p, "halo"), b(p, "bold"), b(p, "italic"));
super::labels::with_class(s, p, |c| {
if let Some(v) = size {
c.size = v;
Expand All @@ -301,10 +301,13 @@ pub fn commands() -> Vec<C> {
if let Some(bb) = bold {
c.bold = bb;
}
if let Some(it) = italic {
c.italic = it;
}
})
})
.when(need_feature_layer)
.params(r##"{"layer"?, "class"?: name (none: the Default class, by whatever name it has), "size"?: number, "color"?: "#RRGGBB"|"#RRGGBBAA", "halo"?: bool, "bold"?: bool}"##),
.params(r##"{"layer"?, "class"?: name (none: the Default class, by whatever name it has), "size"?: number, "color"?: "#RRGGBB"|"#RRGGBBAA", "halo"?: bool, "bold"?: bool, "italic"?: bool}"##),
C::new("layer.definitionQuery", "Definition Query", "Data › Definition Query", |s, p| {
let q = ps(p, "where").map(str::trim).unwrap_or("").to_string();
let id = s.layer_id(p)?;
Expand Down
30 changes: 30 additions & 0 deletions crates/engine/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,34 @@ fn a_reference_scale_is_set_kept_and_undone() {
check(&s);
}

#[test]
fn rivers_are_labelled_along_their_lines_in_italics() {
let mut s = world();
s.run("labels.field", &json!({"layer": "Rivers", "field": "name"})).unwrap();
s.run("labels.placement", &json!({"layer": "Rivers", "position": "curved"})).unwrap();
s.run("labels.style", &json!({"layer": "Rivers", "italic": true})).unwrap();
let check = |s: &Session| {
let c = &s.map().layer_by_name("Rivers").unwrap().labels.default;
assert_eq!((c.placement, c.italic), (rustgis_map::LabelPlacement::Curved, true));
};
check(&s);
let dir = std::env::temp_dir().join(format!("rustgis-line-labels-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("labels.rgis");
s.run("project.saveAs", &json!({"path": path.display().to_string()})).unwrap();
let mut t = Session::new(Project::default());
t.run("project.open", &json!({"path": path.display().to_string()})).unwrap();
check(&t);
t.run("share.exportMap", &json!({"path": dir.join("map.png").display().to_string(), "width": 320, "height": 200})).unwrap();
std::fs::remove_dir_all(&dir).ok();
s.run("labels.placement", &json!({"layer": "Rivers", "position": "parallel"})).unwrap();
assert_eq!(s.map().layer_by_name("Rivers").unwrap().labels.default.placement, rustgis_map::LabelPlacement::Parallel);
// Not slanted unless asked: a project from before reads as upright.
s.run("labels.style", &json!({"layer": "Rivers", "italic": false})).unwrap();
let text = serde_json::to_string(&s.map().layer_by_name("Rivers").unwrap().labels).unwrap();
assert!(!text.contains("italic"), "{text}");
}

#[test]
fn project_save_and_open_roundtrip() {
let mut s = world();
Expand Down Expand Up @@ -1312,6 +1340,8 @@ fn hostile_params_never_panic() {
json!({"type": "albers", "lon_0": f64::MAX, "lat_1": "x", "lat_2": -1e308}),
json!({"kind": "legend", "element": 1, "exclude": [null, 7, "", {"x": 1}]}),
json!({"kind": "text", "element": 1, "font": {"family": "serif"}, "italic": [], "align": 7}),
// A label class placed along lines, with junk the placement and style read after it.
json!({"layer": "Countries", "position": "curved", "italic": "maybe", "size": -1e308}),
// A publication sheet's style, accent and orientation, and a shape's rounded corners.
// (The profile left out or valid, so the style and accent are reached.)
json!({"style": 7, "accent": "#XYZ", "kind": "ellipse", "element": 1, "radius": "x"}),
Expand Down
28 changes: 25 additions & 3 deletions crates/map/src/symbology.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,9 @@ pub struct LabelClass {
/// Where a label sits relative to its feature.
#[serde(default)]
pub placement: LabelPlacement,
/// Slanted (an italic face where the font has one).
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub italic: bool,
}

fn default_class_name() -> String {
Expand Down Expand Up @@ -1183,10 +1186,22 @@ pub enum LabelPlacement {
AboveLeft,
BelowRight,
BelowLeft,
/// Along a line, straight, turned to its direction across the text, at its middle; points
/// and polygons are labelled as `Auto`.
Parallel,
/// Along a line, each character turned with it where it bends, at its middle; points and
/// polygons are labelled as `Auto`.
Curved,
}

impl LabelPlacement {
pub const ALL: [LabelPlacement; 10] = [
/// Whether a line is labelled along it ([`Parallel`](Self::Parallel) or
/// [`Curved`](Self::Curved)).
pub fn along_line(self) -> bool {
matches!(self, LabelPlacement::Parallel | LabelPlacement::Curved)
}

pub const ALL: [LabelPlacement; 12] = [
LabelPlacement::Auto,
LabelPlacement::Center,
LabelPlacement::Above,
Expand All @@ -1197,6 +1212,8 @@ impl LabelPlacement {
LabelPlacement::AboveLeft,
LabelPlacement::BelowRight,
LabelPlacement::BelowLeft,
LabelPlacement::Parallel,
LabelPlacement::Curved,
];
pub fn code(self) -> &'static str {
match self {
Expand All @@ -1210,6 +1227,8 @@ impl LabelPlacement {
LabelPlacement::AboveLeft => "aboveLeft",
LabelPlacement::BelowRight => "belowRight",
LabelPlacement::BelowLeft => "belowLeft",
LabelPlacement::Parallel => "parallel",
LabelPlacement::Curved => "curved",
}
}
pub fn label(self) -> &'static str {
Expand All @@ -1224,6 +1243,8 @@ impl LabelPlacement {
LabelPlacement::AboveLeft => "Above Left",
LabelPlacement::BelowRight => "Below Right",
LabelPlacement::BelowLeft => "Below Left",
LabelPlacement::Parallel => "Parallel",
LabelPlacement::Curved => "Curved",
}
}
pub fn from_code(s: &str) -> Option<LabelPlacement> {
Expand All @@ -1235,8 +1256,8 @@ impl LabelPlacement {
let gap = marker / 2.0 + 2.0;
let (dx, dy) = (w / 2.0 + gap, h / 2.0 + gap);
match self {
LabelPlacement::Auto if is_point => (0.0, -(marker / 2.0 + h * 0.6)),
LabelPlacement::Auto | LabelPlacement::Center => (0.0, 0.0),
LabelPlacement::Auto | LabelPlacement::Parallel | LabelPlacement::Curved if is_point => (0.0, -(marker / 2.0 + h * 0.6)),
LabelPlacement::Auto | LabelPlacement::Center | LabelPlacement::Parallel | LabelPlacement::Curved => (0.0, 0.0),
LabelPlacement::Above => (0.0, -dy),
LabelPlacement::Below => (0.0, dy),
LabelPlacement::Left => (-dx, 0.0),
Expand Down Expand Up @@ -1274,6 +1295,7 @@ impl LabelClass {
closest: None,
farthest: None,
placement: LabelPlacement::Auto,
italic: false,
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ tiny-skia = { workspace = true }
bytemuck = { workspace = true }
lyon = { workspace = true }
ab_glyph = { workspace = true }
unicode-segmentation = { workspace = true }
epaint_default_fonts = { workspace = true }
log = { workspace = true }
image = { workspace = true, features = ["jpeg"] }
Expand Down
Loading
Loading