polygon-art/src/svg.rs

58 lines
2.2 KiB
Rust

use geo_svg_io::geo_svg_reader::svg_d_path_to_geometry;
use geo_types::{Coordinate, LineString, MultiPolygon, Polygon};
use std::error::Error;
use svg::node::element::tag::Path;
use svg::parser::Event;
use svg::Parser;
/// load a multipolygon dynamically at runtime
pub fn load_multipolygon_from_svg(path: &str) -> Result<MultiPolygon<f64>, Box<dyn Error>> {
let mut content = String::new();
let parser = svg::open(path, &mut content)?;
parse_svg_to_multipolygon(parser)
}
/// load a multipolygon statically at compiletime
/// with include_str!
pub fn load_multipolygon_from_svg_include(
content: &str,
) -> Result<MultiPolygon<f64>, Box<dyn Error>> {
let parser = svg::read(content)?;
parse_svg_to_multipolygon(parser)
}
#[allow(non_upper_case_globals)]
fn parse_svg_to_multipolygon(parser: Parser) -> Result<MultiPolygon<f64>, Box<dyn Error>> {
let mut result: Vec<Polygon<f64>> = Vec::new();
for event in parser {
match event {
Event::Tag(Path, _, attributes) => {
let data = attributes.get("d").unwrap();
let parsed_svg = svg_d_path_to_geometry(&data).ok().unwrap();
let polygon = parsed_svg.into_polygon().unwrap();
// both libraries use different versions of geo-types, so we have to map them :facepalm:
// map interiors
let mut interiors: Vec<LineString<f64>> = Vec::new();
for interior_line_string in polygon.interiors().to_vec() {
let mut new_line_string: Vec<Coordinate<f64>> = Vec::new();
for i in interior_line_string.into_points() {
new_line_string.push(Coordinate { x: i.x(), y: i.y() });
}
interiors.push(LineString::from(new_line_string));
}
// map exteriors
let mut l: Vec<Coordinate<f64>> = Vec::new();
for i in polygon.exterior().clone().into_points() {
l.push(Coordinate { x: i.x(), y: i.y() });
}
let exterior = LineString::from(l);
result.push(Polygon::new(exterior, interiors));
}
_ => {}
}
}
Ok(MultiPolygon::from(result))
}