# rust-geo-svg Functionality to convert between SVG and geo-types. ## SVG to Geometry This package provides a functions to read a string containing an SVG element or `d` string and parse it to a geometry. ### svg_to_geometry(svg: &str) **Note** this function does not parse a full SVG string (e.g., ``), it only parses the individual shape elements (e.g., ``). The following SVG elements are supported and produce the specified Geometry types: * \ → Geometry with the autodetected Geometry type * \ → Polygon * \ → LineString * \ → Polygon * \ → Line #### Examples ```rust use geo_types::{ Polygon, polygon }; use geo_svg_io::geo_svg_reader::svg_to_geometry; let poly: Polygon = polygon!( exterior: [ (x: 0.0_f64, y: 0.0), (x: 0.0, y: 60.0), (x: 60.0, y: 60.0), (x: 60.0, y: 0.0), (x: 0.0, y: 0.0),], interiors:[[ (x: 10.0, y: 10.0), (x: 40.0, y: 1.0), (x: 40.0, y: 40.0), (x: 10.50, y: 40.0), (x: 10.0, y: 10.0),] ] ); let svg_string = String::from(r#""#); let parsed_svg = svg_to_geometry(&svg_string); assert!(parsed_svg.is_ok()); let parsed_poly = parsed_svg.ok().unwrap().into_polygon(); assert!(parsed_poly.is_some()); assert_eq!(poly, parsed_poly.unwrap()); ``` ```rust use geo_types::{ Polygon, polygon }; use geo_svg_io::geo_svg_reader::svg_to_geometry; let poly: Polygon = polygon!( exterior: [ (x: 0.0_f64, y: 0.0), (x: 0.0, y: 60.0), (x: 60.0, y: 60.0), (x: 60.0, y: 0.0), (x: 0.0, y: 0.0),], interiors:[] ); let svg_string = String::from(r#""#); let parsed_svg = svg_to_geometry(&svg_string); assert!(parsed_svg.is_ok()); let parsed_poly = parsed_svg.ok().unwrap().into_polygon(); assert!(parsed_poly.is_some()); assert_eq!(poly, parsed_poly.unwrap()); ``` ### svg_to_geometry_collection(svg: &str) **Note** this function does not parse a full SVG string (e.g., ``), it only parses the individual shape elements (e.g., ``). The following SVG elements are supported and produce the specified Geometry types: * \ → GeometryCollection * \ → GeometryCollection with a single Polygon * \ → GeometryCollection with a single LineString * \ → GeometryCollection with a single Polygon * \ → GeometryCollection with a single Line #### Examples ```rust use geo_types::{ Polygon, polygon }; use geo_svg_io::geo_svg_reader::svg_to_geometry_collection; let poly: Polygon = polygon!( exterior: [ (x: 0.0_f64, y: 0.0), (x: 0.0, y: 60.0), (x: 60.0, y: 60.0), (x: 60.0, y: 0.0), (x: 0.0, y: 0.0),], interiors:[[ (x: 10.0, y: 10.0), (x: 40.0, y: 1.0), (x: 40.0, y: 40.0), (x: 10.50, y: 40.0), (x: 10.0, y: 10.0),] ] ) .into(); let svg_string = String::from(r#""#); let parsed_svg = svg_to_geometry_collection(&svg_string); assert!(parsed_svg.is_ok()); // Unwrap the GeometryCollection result let geom = parsed_svg.ok().unwrap(); assert_eq!(1, geom.0.len()); // Read the geometry as a Polygon let pl = geom.0[0].clone().into_polygon(); assert_eq!(true, pl.is_some()); assert_eq!(poly, pl.unwrap()); ``` ```rust use geo_types::{ Polygon, polygon }; use geo_svg_io::geo_svg_reader::svg_to_geometry; let poly: Polygon = polygon!( exterior: [ (x: 0.0_f64, y: 0.0), (x: 0.0, y: 60.0), (x: 60.0, y: 60.0), (x: 60.0, y: 0.0), (x: 0.0, y: 0.0),], interiors:[] ) .into(); let svg_string = String::from(r#""#); let parsed_svg = svg_to_geometry_collection(&svg_string); assert!(parsed_svg.is_ok()); // Unwrap the GeometryCollection result let geom = parsed_svg.ok().unwrap(); assert_eq!(1, geom.0.len()); // Read the geometry as a Polygon let pl = geom.0[0].clone().into_polygon(); assert_eq!(true, pl.is_some()); assert_eq!(poly, pl.unwrap()); ``` ### svg_d_path_to_geometry(svg: &str) A `` element `d` string can be parsed directly into a Geometry by the `svg_d_path_to_geometry(svg: &str)` function. The output will always be a GeometryCollection. #### Examples ```rust use geo_svg_io::geo_svg_reader::svg_d_path_to_geometry_collection; use geo_types::polygon; let poly = polygon!( exterior: [ (x: 0.0, y: 0.0), (x: 0.0, y: 60.0), (x: 60.0, y: 60.0), (x: 60.0, y: 0.0), (x: 0.0, y: 0.0),], interiors:[[ (x: 10.0, y: 10.0), (x: 40.0, y: 1.0), (x: 40.0, y: 40.0), (x: 10.50, y: 40.0), (x: 10.0, y: 10.0),] ] ); let svg_string = String::from("M0 0l0 60l60 0L60 0L0 0M10 10L40 1L40 40L10.5 40L10 10"); let parsed_svg = svg_d_path_to_geometry(&svg_string); assert!(parsed_svg.is_ok()); let pl = parsed_svg.ok().unwrap().into_polygon(); assert!(pl.is_some()); assert_eq!(pl.unwrap(), poly); ``` ### svg_d_path_to_geometry_collection(svg: &str) A `` element `d` string can be parsed directly into a GeometryCollection by the `svg_d_path_to_geometry_collection(svg: &str)` function. The output will always be a GeometryCollection. #### Examples ```rust use geo_svg_io::geo_svg_reader::svg_d_path_to_geometry_collection; use geo_types::polygon; let poly = polygon!( exterior: [ (x: 0.0, y: 0.0), (x: 0.0, y: 60.0), (x: 60.0, y: 60.0), (x: 60.0, y: 0.0), (x: 0.0, y: 0.0),], interiors:[[ (x: 10.0, y: 10.0), (x: 40.0, y: 1.0), (x: 40.0, y: 40.0), (x: 10.50, y: 40.0), (x: 10.0, y: 10.0),] ] ); let svg_string = String::from("M0 0l0 60l60 0L60 0L0 0M10 10L40 1L40 40L10.5 40L10 10"); let parsed_svg = svg_d_path_to_geometry_collection(&svg_string); assert!(parsed_svg.is_ok()); // Unwrap the GeometryCollection result let geom = parsed_svg.ok().unwrap(); assert_eq!(1, geom.0.len()); // Read the geometry as a Polygon let pl = geom.0[0].clone().into_polygon(); assert_eq!(true, pl.is_some()); assert_eq!(pl.unwrap(), poly); ``` ### Error handling Both function return a Result which will either contain the parsed Geometry or an Error of the `SvgError` Enum. An error may result from passing an unsupported SVG element type, from an improperly formed SVG element, or from an inability to parse a `float` from the supplied string. ## Geometry to SVG This package provides two traits for converting a Geometry to SVG. **Note** that the parsing of curves in `` `d`-strings is simplistic. It plots 100 points along the curve. **TODO** update this functionality to use a recursive function instead to create points until they are collinear (enough). ### ToSvg Using `to_svg()` from any Geometry type with produce an SVG element of the simplest type possible: * Polygon → \ * LineString → \ * Line → \ * Triangle → \ with three points * Rect → \ with `x`, `y`, `width`, and `height` Complex Geometry types will return multiple SVG elements separated by `newline`s: * GeometryCollection → `newline` separated SVG elements corresponding to the individual Geometries it contains * MultiPolygon → `newline` separated elements * MultiLineString → `newline` separated elements #### Example ```rust use geo_types::{ MultiPolygon, polygon }; use geo_svg_io::geo_svg_writer::ToSvg; let poly1 = polygon![ (x: 1.0, y: 1.0), (x: 4.0, y: 1.0), (x: 4.0, y: 4.0), (x: 1.0, y: 4.0), (x: 1.0, y: 1.0), ]; let poly2 = polygon!( exterior: [ (x: 0.0, y: 0.0), (x: 6.0, y: 0.0), (x: 6.0, y: 6.0), (x: 0.0, y: 6.0), (x: 0.0, y: 0.0),], interiors:[[ (x: 1.0, y: 1.0), (x: 4.0, y: 1.0), (x: 4.0, y: 4.0), (x: 1.50, y: 4.0), (x: 1.0, y: 1.0),] ] ); let mp = MultiPolygon(vec![poly1, poly2]); let wkt_out = mp.to_svg(); let expected = String::from( r#" "#, ); assert_eq!(wkt_out, expected); ``` ### ToSvgString Using `to_svg_string()` from any Geometry type will produce an SVG `d` string for all the points of that geometry, which can be used in an SVG path element #### Examples ```rust use geo_types::{polygon, MultiPolygon}; use geo_svg_io::geo_svg_writer::ToSvgString; let poly1 = polygon![ (x: 1.0, y: 1.0), (x: 4.0, y: 1.0), (x: 4.0, y: 4.0), (x: 1.0, y: 4.0), (x: 1.0, y: 1.0), ]; let poly2 = polygon!( exterior: [ (x: 0.0, y: 0.0), (x: 6.0, y: 0.0), (x: 6.0, y: 6.0), (x: 0.0, y: 6.0), (x: 0.0, y: 0.0),], interiors:[[ (x: 1.0, y: 1.0), (x: 4.0, y: 1.0), (x: 4.0, y: 4.0), (x: 1.50, y: 4.0), (x: 1.0, y: 1.0),] ] ); let mp = MultiPolygon(vec![poly1, poly2]); let wkt_out = mp.to_svg_string(); let expected = String::from( "M1 1L4 1L4 4L1 4L1 1M0 0L6 0L6 6L0 6L0 0M1 1L4 1L4 4L1.5 4L1 1" ); assert_eq!(wkt_out, expected); ``` # Similar projects For a similar project that provides higher level functionality to build SVG's from Rust geo-types, see https://github.com/lelongg/geo-svg