/*
* Copyright (c) 2017 wetransform GmbH
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* wetransform GmbH <http://www.wetransform.to>
*/
package eu.esdihumboldt.util.svg.test;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.batik.dom.GenericDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
* Helper for painting (geometries) to an SVG image.
*
* @author Simon Templer
*/
public class SVGPainter {
private final PaintSettings settings;
private final SVGGraphics2D g;
/**
* Create a new painter.
*
* @param settings the paint settings
*/
public SVGPainter(PaintSettings settings) {
this.settings = settings;
this.g = createSVGGraphics();
if (settings.getCanvasSize() != null) {
g.setSVGCanvasSize(settings.getCanvasSize());
}
}
/**
* Set the canvas size for the SVG image.
*
* @param width the width
* @param height the height
*/
public void setCanvasSize(int width, int height) {
g.setSVGCanvasSize(new Dimension(width, height));
}
private SVGGraphics2D createSVGGraphics() {
// Get a DOMImplementation.
DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
// Create an instance of org.w3c.dom.Document.
String svgNS = "http://www.w3.org/2000/svg";
Document document = domImpl.createDocument(svgNS, "svg", null);
// Create an instance of the SVG Generator.
return new SVGGraphics2D(document);
}
/**
* Write the graphics to a SVG file.
*
* @param file the file to write to
* @throws IOException if an error occurs writing the file
*/
public void writeToFile(Path file) throws IOException {
// Finally, stream out SVG to the standard output using
// UTF-8 encoding.
boolean useCSS = true; // we want to use CSS style attributes
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
g.stream(writer, useCSS);
}
}
/**
* Write the SVG to a string.
*
* @return the SVG as string
* @throws IOException if creating the string representation fails
*/
public String writeToString() throws IOException {
boolean useCSS = true; // we want to use CSS style attributes
StringWriter writer = new StringWriter();
g.stream(writer, useCSS);
return writer.toString();
}
/**
* Draw a geometry.
*
* @param geometry the geometry to draw
*/
public void drawGeometry(Geometry geometry) {
if (geometry == null || geometry.isEmpty()) {
return;
}
if (geometry.getNumGeometries() > 1) {
for (int i = 0; i < geometry.getNumGeometries(); i++) {
drawGeometry(geometry.getGeometryN(i));
}
}
else {
if (geometry instanceof Polygon) {
drawPolygon((Polygon) geometry);
}
else if (geometry instanceof LineString) {
drawLineString((LineString) geometry);
}
else if (geometry instanceof Point) {
drawPoint((Point) geometry);
}
else {
throw new IllegalArgumentException(
"Cannot draw geometry of type " + geometry.getClass().getName());
}
}
}
/**
* Draw a line string.
*
* @param geometry the line string geometry
*/
public void drawLineString(LineString geometry) {
Coordinate[] coords = geometry.getCoordinates();
if (coords.length >= 2) {
for (int i = 0; i < coords.length - 1; i++) {
g.drawLine( //
settings.convertX(coords[i].x), //
settings.convertY(coords[i].y), //
settings.convertX(coords[i + 1].x), //
settings.convertY(coords[i + 1].y));
}
}
}
/**
* Draw a line between two points.
*
* @param p1 the first point
* @param p2 the second point
*/
public void drawLine(Coordinate p1, Coordinate p2) {
g.drawLine( //
settings.convertX(p1.x), //
settings.convertY(p1.y), //
settings.convertX(p2.x), //
settings.convertY(p2.y));
}
/**
* Draw a polygon.
*
* @param geometry the polygon geometry
*/
public void drawPolygon(Polygon geometry) {
// exterior
Coordinate[] coordinates = geometry.getExteriorRing().getCoordinates();
java.awt.Polygon outerPolygon = createPolygon(coordinates);
if (geometry.getNumInteriorRing() > 0) {
// polygon has interior geometries
java.awt.geom.Area drawArea = new java.awt.geom.Area(outerPolygon);
// interior
for (int i = 0; i < geometry.getNumInteriorRing(); i++) {
LineString interior = geometry.getInteriorRingN(i);
java.awt.Polygon innerPolygon = createPolygon(interior.getCoordinates());
drawArea.subtract(new java.awt.geom.Area(innerPolygon));
}
g.draw(drawArea);
}
else {
// polygon has no interior
// use polygon instead of Area for painting, as painting small
// Areas sometimes produces strange results (some are not
// visible)
g.draw(outerPolygon);
}
}
private java.awt.Polygon createPolygon(Coordinate[] coordinates) {
java.awt.Polygon result = new java.awt.Polygon();
for (Coordinate coord : coordinates) {
result.addPoint(settings.convertX(coord.x), settings.convertY(coord.y));
}
return result;
}
/**
* Get the internal graphics object for direct interaction.
*
* @return the internal graphics object
*/
public Graphics2D getGraphics2D() {
return g;
}
/**
* Draw a point.
*
* @param point the point geometry
*/
public void drawPoint(Point point) {
drawPoint(point.getCoordinate());
}
/**
* Draw a point.
*
* @param coord the point coordinates
*/
public void drawPoint(Coordinate coord) {
g.fillOval(settings.convertX(coord.x) - settings.getPointSize() / 2, //
settings.convertY(coord.y) - settings.getPointSize() / 2, //
settings.getPointSize(), //
settings.getPointSize());
}
/**
* Set the drawing color. Convenience method that delegates the call to the
* internal graphics object.
*
* @param color the color to set
*/
public void setColor(Color color) {
g.setColor(color);
}
/**
* Set the stroke width.
*
* @param width the stroke width to set
*/
public void setStroke(float width) {
g.setStroke(new BasicStroke(width));
}
/**
* @return the settings
*/
public PaintSettings getSettings() {
return settings;
}
}