use crate::error::Result;
use crate::projection::transform_coordinates_to_pixels;
use crate::svg::types::SvgContext;
use crate::svg::utils::get_property_str;
use geo_types::{Coord, LineString, MultiLineString};
use geojson::Feature;
pub fn generate_linestring_svg_items(
feature: &Feature,
linestring: &LineString<f64>,
context: &SvgContext,
) -> Result<Vec<String>> {
let coords: Vec<Coord<f64>> = linestring.coords().copied().collect();
let transformed_points = transform_coordinates_to_pixels(
&coords,
&context.mercator_bbox,
context.size,
context.x_scaling_factor,
context.y_scaling_factor,
0.0,
)?;
let stroke = get_property_str(feature, "stroke", "black");
let stroke_width = get_property_str(feature, "stroke-width", "1");
let stroke_opacity = get_property_str(feature, "stroke-opacity", "1.0");
let points_string = transformed_points
.iter()
.map(|coord| format!("{},{}", coord.x, coord.y))
.collect::<Vec<_>>()
.join(" ");
Ok(vec![format!(
r#"<polyline points="{}" fill="none" stroke="{}" stroke-width="{}" stroke-opacity="{}" />"#,
points_string, stroke, stroke_width, stroke_opacity
)])
}
pub fn generate_multi_linestring_svg_items(
feature: &Feature,
multi_linestring: &MultiLineString<f64>,
context: &SvgContext,
) -> Result<Vec<String>> {
let mut svg_items = Vec::new();
let stroke = get_property_str(feature, "stroke", "black");
let stroke_width = get_property_str(feature, "stroke-width", "1");
let stroke_opacity = get_property_str(feature, "stroke-opacity", "1.0");
for linestring in multi_linestring.0.iter() {
let coords: Vec<Coord<f64>> = linestring.coords().copied().collect();
let transformed_points = transform_coordinates_to_pixels(
&coords,
&context.mercator_bbox,
context.size,
context.x_scaling_factor,
context.y_scaling_factor,
0.0,
)?;
let points_string = transformed_points
.iter()
.map(|coord| format!("{},{}", coord.x, coord.y))
.collect::<Vec<_>>()
.join(" ");
svg_items.push(format!(
r#"<polyline points="{}" fill="none" stroke="{}" stroke-width="{}" stroke-opacity="{}" />"#,
points_string, stroke, stroke_width, stroke_opacity
));
}
Ok(svg_items)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::projection::BoundingBox;
use geo_types::Polygon;
use serde_json::json;
fn create_test_context() -> SvgContext {
let bbox = BoundingBox::new(-1.0, -1.0, 1.0, 1.0);
let image_polygon = Polygon::new(
geo_types::LineString::from(vec![
Coord { x: -1.0, y: -1.0 },
Coord { x: 1.0, y: -1.0 },
Coord { x: 1.0, y: 1.0 },
Coord { x: -1.0, y: 1.0 },
Coord { x: -1.0, y: -1.0 },
]),
vec![],
);
SvgContext::new(bbox, 256.0, 128.0, 128.0, image_polygon)
}
#[test]
fn test_generate_linestring_svg_items_basic() {
let context = create_test_context();
let linestring = LineString::from(vec![
Coord { x: -0.5, y: -0.5 },
Coord { x: 0.0, y: 0.0 },
Coord { x: 0.5, y: 0.5 },
]);
let feature: Feature = serde_json::from_value(json!({
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": []},
"properties": {}
}))
.unwrap();
let items = generate_linestring_svg_items(&feature, &linestring, &context).unwrap();
assert_eq!(items.len(), 1);
assert!(items[0].contains("<polyline"));
assert!(items[0].contains("stroke=\"black\""));
assert!(items[0].contains("stroke-width=\"1\""));
assert!(items[0].contains("fill=\"none\""));
}
#[test]
fn test_generate_linestring_svg_items_with_style() {
let context = create_test_context();
let linestring = LineString::from(vec![
Coord { x: -0.5, y: -0.5 },
Coord { x: 0.5, y: 0.5 },
]);
let feature: Feature = serde_json::from_value(json!({
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": []},
"properties": {
"stroke": "red",
"stroke-width": "2",
"stroke-opacity": "0.8"
}
}))
.unwrap();
let items = generate_linestring_svg_items(&feature, &linestring, &context).unwrap();
assert_eq!(items.len(), 1);
assert!(items[0].contains("stroke=\"red\""));
assert!(items[0].contains("stroke-width=\"2\""));
assert!(items[0].contains("stroke-opacity=\"0.8\""));
}
#[test]
fn test_generate_multi_linestring_svg_items() {
let context = create_test_context();
let multi_linestring = MultiLineString(vec![
LineString::from(vec![Coord { x: -0.8, y: -0.8 }, Coord { x: -0.2, y: -0.2 }]),
LineString::from(vec![Coord { x: 0.2, y: 0.2 }, Coord { x: 0.8, y: 0.8 }]),
]);
let feature: Feature = serde_json::from_value(json!({
"type": "Feature",
"geometry": {"type": "MultiLineString", "coordinates": []},
"properties": {}
}))
.unwrap();
let items = generate_multi_linestring_svg_items(&feature, &multi_linestring, &context).unwrap();
assert_eq!(items.len(), 2);
for item in items {
assert!(item.contains("<polyline"));
}
}
}