/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jgrasstools.gears.utils.images;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Stream;
import javax.imageio.ImageIO;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.wms.WebMapServer;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.gce.grassraster.GrassCoverageReader;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.GridCoverageLayer;
import org.geotools.map.GridReaderLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.map.WMSLayer;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.ColorMap;
import org.geotools.styling.ColorMapEntry;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.StyleFactory;
import org.jgrasstools.gears.io.grasslegacy.map.color.ColorRule;
import org.jgrasstools.gears.io.grasslegacy.map.color.GrassColorTable;
import org.jgrasstools.gears.io.rasterreader.OmsRasterReader;
import org.jgrasstools.gears.libs.monitor.DummyProgressMonitor;
import org.jgrasstools.gears.libs.monitor.IJGTProgressMonitor;
import org.jgrasstools.gears.utils.RegionMap;
import org.jgrasstools.gears.utils.SldUtilities;
import org.jgrasstools.gears.utils.colors.ColorUtilities;
import org.jgrasstools.gears.utils.coverage.CoverageUtilities;
import org.jgrasstools.gears.utils.files.FileUtilities;
import org.jgrasstools.gears.utils.geometry.GeometryUtilities;
import org.opengis.filter.expression.Expression;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
/**
* An utility class for simple image map generation.
*
* <p>A sample usage could be the overlay of vector layers.
* <pre>
* ImageGenerator imgGen = new ImageGenerator();
* imgGen.addFeaturePath(reticolo, null);
* imgGen.setLayers();
* imgGen.dumpPngImage(imagePath, bounds, 300, 300, 100);
* </pre>
*
* @author Andrea Antonello (www.hydrologis.com)
* @since 0.7.3
*/
public class ImageGenerator {
public boolean doLegacyGrass = false;
private String wmsURL = null;
private List<String> featurePaths = new ArrayList<String>();
private List<String> featureFilter = new ArrayList<String>();
private List<String> coveragePaths = new ArrayList<String>();
private List<GridGeometry2D> coverageRegions = new ArrayList<GridGeometry2D>();
// private AffineTransform worldToScreen;
// private AffineTransform screenToWorld;
private StyleFactory sf;
private IJGTProgressMonitor monitor = new DummyProgressMonitor();
private List<Layer> layers = new ArrayList<Layer>();
private List<Layer> synchronizedLayers = null;
private MapContent content;
private StreamingRenderer renderer;
private File shapesFile;
private CoordinateReferenceSystem forceCrs;
public ImageGenerator( IJGTProgressMonitor monitor , CoordinateReferenceSystem forceCrs) {
this.forceCrs = forceCrs;
if (monitor != null)
this.monitor = monitor;
sf = CommonFactoryFinder.getStyleFactory(null);
}
public void setDoLegacyGrass( boolean doLegacyGrass ) {
this.doLegacyGrass = doLegacyGrass;
}
/**
* Add a new coverage file path.
*
* <p>The order will be considered. First paths are drawn first.</p>
*
* @param coveragePath the path to add.
*/
public void addCoveragePath( String coveragePath ) {
if (!coveragePaths.contains(coveragePath)) {
coveragePaths.add(coveragePath);
}
}
/**
* Add a coverage read region (this has to have same index as addCoveragePath.
*
* @param coverageRegion the region to read.
*/
public void addCoverageRegion( GridGeometry2D coverageRegion ) {
if (!coverageRegions.contains(coverageRegion)) {
coverageRegions.add(coverageRegion);
}
}
/**
* Set a WMS service to handle as raster layer.
*
* @param wmsURL the WMS url and layer name in the format: http://wmsurl#layername
*/
public void setWMS( String wmsURL ) {
this.wmsURL = wmsURL;
}
/**
* Add a new feature file path.
*
* <p>The order will be considered. First paths are drawn first.</p>
*
* @param featurePath the path to add.
*/
public void addFeaturePath( String featurePath, String filter ) {
if (!featurePaths.contains(featurePath)) {
featurePaths.add(featurePath);
if (filter == null) {
filter = "";
}
featureFilter.add(filter);
}
}
/**
* Method to add a file that contains a list of shapes/texts to add.
*
* <p><b>This is applied on top of the final image. Positions are in pixel.</b>
* </p>
* </p>
* <p>
* Supported are:</br>
* <ul>
* <li>text;x;y;mytext;colorrgba;size</li>
* <li>box;x;y;w;h;strokewidth;fillrgba;strokergba</li>
* <li>roundedbox;x;y;w;h;round;strokewidth;fillrgba;strokergba</li>
* <li>...</li>
* </ul>
* </p>
*
*/
public void addShapesPath( String shapesPath ) {
this.shapesFile = new File(shapesPath);
}
// private void setTransforms( final ReferencedEnvelope envelope, int width, int height ) {
//
// double envWidth = envelope.getWidth();
// double envHeight = envelope.getHeight();
// double envValue = envWidth;
// if (envHeight > envWidth) {
// envValue = envHeight;
// }
//
// double xscale = width / envValue;
// double yscale = height / envValue;
//
// double median0 = envelope.getMedian(0);
// double xoff = median0 * xscale - width / 2.0;
// double median1 = envelope.getMedian(1);
// double yoff = median1 * yscale + height / 2.0;
//
// worldToScreen = new AffineTransform(xscale, 0, 0, -yscale, -xoff, yoff);
// }
/**
* Set the layers that have to be drawn.
*
* <p><b>This has to be called before the drawing process.</p>
* @return the max envelope of the data.
* @throws Exception
*/
public ReferencedEnvelope setLayers() throws Exception {
ReferencedEnvelope maxExtent = null;
// wms first
if (wmsURL != null) {
String[] split = wmsURL.split("#");
// setup the reader
WebMapServer server = new WebMapServer(new URL(split[0]));
AbstractGridCoverage2DReader reader = new WMSLayer(server, getWMSLayer(server, split[1])).getReader();
RasterSymbolizer sym = sf.getDefaultRasterSymbolizer();
Style style = SLD.wrapSymbolizers(sym);
GridReaderLayer layer = new GridReaderLayer(reader, style);
layers.add(layer);
GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope();
if (originalEnvelope != null) {
if (maxExtent == null) {
maxExtent = new ReferencedEnvelope(originalEnvelope.getCoordinateReferenceSystem());
}
expandToIncludeEnvelope(maxExtent, originalEnvelope);
}
}
// coverages
monitor.beginTask("Reading raster maps...", coveragePaths.size());
for( int r = 0; r < coveragePaths.size(); r++ ) {
String coveragePath = coveragePaths.get(r);
GridGeometry2D region = null;
if (coverageRegions != null && coverageRegions.size() == coveragePaths.size()) {
region = coverageRegions.get(r);
}
File file = new File(coveragePath);
GridCoverage2D raster = null;
AbstractGridCoverage2DReader reader = null;
try {
try {
// first try a format that gives back a reader
AbstractGridFormat format = GridFormatFinder.findFormat(file);
reader = format.getReader(file);
if (reader instanceof GrassCoverageReader) {
reader = null;
}
} catch (Exception e1) {
// ignore and try others
}
if (reader == null) {
if (region == null) {
raster = OmsRasterReader.readRaster(coveragePath);
} else {
RegionMap regionMap = CoverageUtilities.gridGeometry2RegionParamsMap(region);
double n = regionMap.getNorth();
double s = regionMap.getSouth();
double w = regionMap.getWest();
double e = regionMap.getEast();
double xres = regionMap.getXres();
double yres = regionMap.getYres();
OmsRasterReader rreader = new OmsRasterReader();
rreader.file = coveragePath;
rreader.pNorth = n;
rreader.pSouth = s;
rreader.pWest = w;
rreader.pEast = e;
rreader.pXres = xres;
rreader.pYres = yres;
rreader.doLegacyGrass = doLegacyGrass;
rreader.process();
raster = rreader.outRaster;
}
}
// if (crs == null) {
// crs = raster.getCoordinateReferenceSystem();
// }
} catch (Exception e) {
monitor.errorMessage(e.getLocalizedMessage());
monitor.errorMessage("Trying to find other coverage source...");
// try with available readers
try {
AbstractGridFormat format = GridFormatFinder.findFormat(file);
reader = format.getReader(file);
// if (crs == null) {
// crs = reader.getCrs();
// }
} catch (Exception ex) {
throw ex;
}
}
File styleFile = FileUtilities.substituteExtention(file, "sld");
Style style;
if (styleFile.exists()) {
style = SldUtilities.getStyleFromFile(styleFile);
} else {
if (CoverageUtilities.isGrass(coveragePath)) {
style = getGrassStyle(coveragePath);
} else {
RasterSymbolizer sym = sf.getDefaultRasterSymbolizer();
style = SLD.wrapSymbolizers(sym);
}
}
if (raster != null) {
GridCoverageLayer layer = new GridCoverageLayer(raster, style);
layers.add(layer);
org.opengis.geometry.Envelope envelope = raster.getEnvelope();
if (maxExtent == null) {
maxExtent = new ReferencedEnvelope(envelope.getCoordinateReferenceSystem());
}
expandToIncludeEnvelope(maxExtent, envelope);
}
if (reader != null) {
GridReaderLayer layer = new GridReaderLayer(reader, style);
// SimpleFeatureSource featureSource = layer.getFeatureSource();
layers.add(layer);
org.opengis.geometry.Envelope envelope = reader.getOriginalEnvelope();
if (maxExtent == null) {
maxExtent = new ReferencedEnvelope(envelope.getCoordinateReferenceSystem());
}
expandToIncludeEnvelope(maxExtent, envelope);
}
monitor.worked(1);
}
monitor.done();
monitor.beginTask("Reading vector maps...", featurePaths.size());
for( int i = 0; i < featurePaths.size(); i++ ) {
String featurePath = featurePaths.get(i);
String filter = featureFilter.get(i);
FileDataStore store = FileDataStoreFinder.getDataStore(new File(featurePath));
SimpleFeatureSource featureSource = store.getFeatureSource();
SimpleFeatureCollection featureCollection;
if (filter.length() == 0) {
featureCollection = featureSource.getFeatures();
} else {
featureCollection = featureSource.getFeatures(ECQL.toFilter(filter));
}
// if (crs == null) {
// crs = featureSource.getSchema().getCoordinateReferenceSystem();
// }
File styleFile = FileUtilities.substituteExtention(new File(featurePath), "sld");
Style style;
if (styleFile.exists()) {
style = SldUtilities.getStyleFromFile(styleFile);
} else {
style = SLD.createSimpleStyle(featureSource.getSchema());
}
FeatureLayer layer = new FeatureLayer(featureCollection, style);
layers.add(layer);
if (maxExtent == null) {
maxExtent = new ReferencedEnvelope(featureCollection.getSchema().getCoordinateReferenceSystem());
}
expandToIncludeEnvelope(maxExtent, featureCollection.getBounds());
monitor.worked(1);
}
synchronizedLayers = Collections.synchronizedList(layers);
monitor.done();
return maxExtent;
}
private void expandToIncludeEnvelope( ReferencedEnvelope maxExtent, org.opengis.geometry.Envelope envelope ) {
ReferencedEnvelope tmpExtent = new ReferencedEnvelope(envelope.getCoordinateReferenceSystem());
DirectPosition ll = envelope.getLowerCorner();
double[] coordinate = ll.getCoordinate();
tmpExtent.expandToInclude(new Coordinate(coordinate[0], coordinate[1]));
DirectPosition ur = envelope.getUpperCorner();
coordinate = ur.getCoordinate();
tmpExtent.expandToInclude(new Coordinate(coordinate[0], coordinate[1]));
try {
ReferencedEnvelope transformed = tmpExtent.transform(maxExtent.getCoordinateReferenceSystem(), true);
maxExtent.expandToInclude(transformed);
} catch (TransformException | FactoryException e) {
e.printStackTrace();
}
}
private org.geotools.data.ows.Layer getWMSLayer( WebMapServer server, String layerName ) {
for( org.geotools.data.ows.Layer layer : server.getCapabilities().getLayerList() ) {
if (layerName.equals(layer.getName())) {
return layer;
}
}
throw new IllegalArgumentException("Could not find layer " + layerName);
}
private synchronized void checkMapContent() {
if (content == null) {
content = new MapContent();
content.setTitle("dump");
for( Layer layer : layers ) {
content.addLayer(layer);
}
renderer = new StreamingRenderer();
renderer.setMapContent(content);
}
}
/**
* Draw the map on an image.
*
* @param bounds the area of interest.
* @param imageWidth the width of the image to produce.
* @param imageHeight the height of the image to produce.
* @param buffer the buffer to add around the map bounds in map units.
* @return the image.
*/
public BufferedImage drawImage( ReferencedEnvelope ref, int imageWidth, int imageHeight, double buffer ) {
checkMapContent();
if (buffer > 0.0)
ref.expandBy(buffer, buffer);
Rectangle2D refRect = new Rectangle2D.Double(ref.getMinX(), ref.getMinY(), ref.getWidth(), ref.getHeight());
Rectangle2D imageRect = new Rectangle2D.Double(0, 0, imageWidth, imageHeight);
GeometryUtilities.scaleToRatio(imageRect, refRect, false);
ReferencedEnvelope newRef = new ReferencedEnvelope(refRect, ref.getCoordinateReferenceSystem());
Rectangle imageBounds = new Rectangle(0, 0, imageWidth, imageHeight);
BufferedImage dumpImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = dumpImage.createGraphics();
g2d.fillRect(0, 0, imageWidth, imageHeight);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
synchronized (renderer) {
renderer.paint(g2d, imageBounds, newRef);
}
return dumpImage;
}
public void drawImage( Graphics2D g2d, ReferencedEnvelope ref, int imageWidth, int imageHeight, double buffer ) {
checkMapContent();
if (buffer > 0.0)
ref.expandBy(buffer, buffer);
Rectangle2D refRect = new Rectangle2D.Double(ref.getMinX(), ref.getMinY(), ref.getWidth(), ref.getHeight());
Rectangle2D imageRect = new Rectangle2D.Double(0, 0, imageWidth, imageHeight);
GeometryUtilities.scaleToRatio(imageRect, refRect, false);
ReferencedEnvelope newRef = new ReferencedEnvelope(refRect, ref.getCoordinateReferenceSystem());
Rectangle imageBounds = new Rectangle(0, 0, imageWidth, imageHeight);
Color white = Color.white;
g2d.setColor(new Color(white.getRed(), white.getGreen(), white.getBlue(), 0));
g2d.fillRect(0, 0, imageWidth, imageHeight);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
synchronized (renderer) {
content.getViewport().setBounds(newRef);
renderer.paint(g2d, imageBounds, newRef);
}
}
/**
* Draw the map on an image creating a new MapContent.
*
* @param bounds the area of interest.
* @param imageWidth the width of the image to produce.
* @param imageHeight the height of the image to produce.
* @param buffer the buffer to add around the map bounds in map units.
* @return the image.
*/
public BufferedImage drawImageWithNewMapContent( ReferencedEnvelope ref, int imageWidth, int imageHeight, double buffer ) {
MapContent content = new MapContent();
content.setTitle("dump");
if (forceCrs!=null) {
content.getViewport().setCoordinateReferenceSystem(forceCrs);
}
synchronized (synchronizedLayers) {
for( Layer layer : synchronizedLayers ) {
content.addLayer(layer);
}
}
StreamingRenderer renderer = new StreamingRenderer();
renderer.setMapContent(content);
if (buffer > 0.0) {
ref = new ReferencedEnvelope(ref);
ref.expandBy(buffer, buffer);
}
double envW = ref.getWidth();
double envH = ref.getHeight();
if (envW < envH) {
double newEnvW = envH * (double) imageWidth / (double) imageHeight;
double delta = newEnvW - envW;
ref.expandBy(delta / 2, 0);
} else {
double newEnvH = envW * (double) imageHeight / (double) imageWidth;
double delta = newEnvH - envH;
ref.expandBy(0, delta / 2.0);
}
Rectangle imageBounds = new Rectangle(0, 0, imageWidth, imageHeight);
BufferedImage dumpImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = dumpImage.createGraphics();
g2d.fillRect(0, 0, imageWidth, imageHeight);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
renderer.paint(g2d, imageBounds, ref);
return dumpImage;
}
public void dispose() {
if (content != null)
content.dispose();
}
/**
* Writes an image of maps drawn to a png file.
*
* @param imagePath the path to which to write the image.
* @param bounds the area of interest.
* @param imageWidth the width of the image to produce.
* @param imageHeight the height of the image to produce.
* @param buffer the buffer to add around the map bounds in map units.
* @param rgbCheck an rgb tripled. If not <code>null</code> and the image generated is
* composed only of that color, then the tile is not generated.
* This can be useful to avoid generation of empty tiles.
* @throws IOException
*/
public void dumpPngImage( String imagePath, ReferencedEnvelope bounds, int imageWidth, int imageHeight, double buffer,
int[] rgbCheck ) throws IOException {
BufferedImage dumpImage = drawImageWithNewMapContent(bounds, imageWidth, imageHeight, buffer);
boolean dumpIt = true;
if (rgbCheck != null)
dumpIt = !isAllOfCheckColor(rgbCheck, dumpImage);
if (dumpIt)
ImageIO.write(dumpImage, "png", new File(imagePath)); //$NON-NLS-1$
}
/**
* Writes an image of maps drawn to a jpg file.
*
* @param imagePath the path to which to write the image.
* @param bounds the area of interest.
* @param imageWidth the width of the image to produce.
* @param imageHeight the height of the image to produce.
* @param buffer the buffer to add around the map bounds in map units.
* @param rgbCheck an rgb tripled. If not <code>null</code> and the image generated is
* composed only of that color, then the tile is not generated.
* This can be useful to avoid generation of empty tiles.
* @throws IOException
*/
public void dumpJpgImage( String imagePath, ReferencedEnvelope bounds, int imageWidth, int imageHeight, double buffer,
int[] rgbCheck ) throws IOException {
BufferedImage dumpImage = drawImageWithNewMapContent(bounds, imageWidth, imageHeight, buffer);
boolean dumpIt = true;
if (rgbCheck != null)
dumpIt = !isAllOfCheckColor(rgbCheck, dumpImage);
if (dumpIt)
ImageIO.write(dumpImage, "jpg", new File(imagePath));
}
/**
* Draw the map on an image.
*
* @param bounds the area of interest.
* @param imageWidth the width of the image to produce.
* @param imageHeight the height of the image to produce.
* @param buffer the buffer to add around the map bounds in map units.
* @param rgbCheck an rgb tripled. If not <code>null</code> and the image generated is
* composed only of that color, then the tile is not generated.
* This can be useful to avoid generation of empty tiles.
* @throws IOException
*/
public BufferedImage getImageWithCheck( ReferencedEnvelope bounds, int imageWidth, int imageHeight, double buffer,
int[] rgbCheck ) throws IOException {
BufferedImage dumpImage = drawImageWithNewMapContent(bounds, imageWidth, imageHeight, buffer);
boolean dumpIt = true;
if (rgbCheck != null)
dumpIt = !isAllOfCheckColor(rgbCheck, dumpImage);
if (dumpIt) {
return dumpImage;
} else {
return null;
}
}
private boolean isAllOfCheckColor( int[] rgbCheck, BufferedImage dumpImage ) {
WritableRaster raster = dumpImage.getRaster();
for( int i = 0; i < raster.getWidth(); i++ ) {
for( int j = 0; j < raster.getHeight(); j++ ) {
int[] value = raster.getPixel(i, j, (int[]) null);
if (value[0] != rgbCheck[0] || value[1] != rgbCheck[1] || value[2] != rgbCheck[2]) {
return false;
}
}
}
return true;
}
/**
* Create an image for a given paper size and scale.
*
* @param imagePath the path to which to write the image.
* @param bounds the area of interest. In this case only the center is considered. The bounds
* are recalculated based in paper size and scale.
* @param scale the scale wanted for the map.
* @param paperFormat the paper format to use.
* @param dpi the wanted dpi. If <code>null</code>, 72dpi is used as default.
* @param legend an optional legend {@link BufferedImage image}.
* @param legendX the X position of the legend in the final image.
* @param legendY the Y position of the legend in the final image.
* @param scalePrefix if not <code>null</code>, this string will be added before the scale definition.
* If <code>null</code>, no scale definition will be added.
* @param scaleSize a size for the scale.
* @param scaleX the X position of the scale in the final image.
* @param scaleY the X position of the scale in the final image.
* @throws Exception
* @since 0.7.6
*/
public void dumpPngImageForScaleAndPaper( String imagePath, ReferencedEnvelope bounds, double scale, EPaperFormat paperFormat,
Double dpi, BufferedImage legend, int legendX, int legendY, String scalePrefix, float scaleSize, int scaleX,
int scaleY ) throws Exception {
if (dpi == null) {
dpi = 72.0;
}
// we use the bounds top find the center
Coordinate centre = bounds.centre();
double boundsXExtension = paperFormat.width() / 1000.0 * scale;
double boundsYExtension = paperFormat.height() / 1000.0 * scale;
Coordinate ll = new Coordinate(centre.x - boundsXExtension / 2.0, centre.y - boundsYExtension / 2.0);
Coordinate ur = new Coordinate(centre.x + boundsXExtension / 2.0, centre.y + boundsYExtension / 2.0);
Envelope tmpEnv = new Envelope(ll, ur);
bounds = new ReferencedEnvelope(tmpEnv, bounds.getCoordinateReferenceSystem());
int imageWidth = (int) (paperFormat.width() / 25.4 * dpi);
int imageHeight = (int) (paperFormat.height() / 25.4 * dpi);
BufferedImage dumpImage = drawImage(bounds, imageWidth, imageHeight, 0);
Graphics2D graphics = (Graphics2D) dumpImage.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (shapesFile != null && shapesFile.exists()) {
applyShapes(graphics);
}
if (legend != null) {
graphics.drawImage(legend, null, legendX, legendY);
}
if (scalePrefix != null) {
Font scaleFont = graphics.getFont().deriveFont(scaleSize);
graphics.setFont(scaleFont);
FontMetrics fontMetrics = graphics.getFontMetrics(scaleFont);
String scaleString = scalePrefix + "1:" + (int) scale;
Rectangle2D stringBounds = fontMetrics.getStringBounds(scaleString, graphics);
double width = stringBounds.getWidth();
double height = stringBounds.getHeight();
graphics.setColor(Color.white);
double border = 5;
graphics.fillRect((int) scaleX, (int) (scaleY - height + 2 * border), (int) (width + 3 * border),
(int) (height + 2 * border));
graphics.setColor(Color.black);
graphics.drawString(scaleString, (int) scaleX + 5, (int) scaleY);
}
ImageIO.write(dumpImage, "png", new File(imagePath));
}
/**
* Create an image for a given paper size and scale.
*
* @param imagePath the path to which to write the image.
* @param bounds the area of interest. In this case only the center is considered. The bounds
* are recalculated based in paper size and scale.
* @param scale the scale wanted for the map.
* @param paperFormat the paper format to use.
* @param dpi the wanted dpi. If <code>null</code>, 72dpi is used as default.
* @param legend an optional legend {@link BufferedImage image}.
* @param legendX the X position of the legend in the final image.
* @param legendY the Y position of the legend in the final image.
* @param scalePrefix if not <code>null</code>, this string will be added before the scale definition.
* If <code>null</code>, no scale definition will be added.
* @param scaleSize a size for the scale.
* @param scaleX the X position of the scale in the final image.
* @param scaleY the X position of the scale in the final image.
* @throws Exception
* @since 0.7.6
*/
public void dump2Graphics2D( Graphics2D graphics2d, ReferencedEnvelope bounds, double scale, EPaperFormat paperFormat,
Double dpi, BufferedImage legend, int legendX, int legendY, String scalePrefix, float scaleSize, int scaleX,
int scaleY ) throws Exception {
if (dpi == null) {
dpi = 72.0;
}
// we use the bounds top find the center
Coordinate centre = bounds.centre();
double boundsXExtension = paperFormat.width() / 1000.0 * scale;
double boundsYExtension = paperFormat.height() / 1000.0 * scale;
Coordinate ll = new Coordinate(centre.x - boundsXExtension / 2.0, centre.y - boundsYExtension / 2.0);
Coordinate ur = new Coordinate(centre.x + boundsXExtension / 2.0, centre.y + boundsYExtension / 2.0);
Envelope tmpEnv = new Envelope(ll, ur);
// tmpEnv.expandBy(1000);
bounds = new ReferencedEnvelope(tmpEnv, bounds.getCoordinateReferenceSystem());
int imageWidth = (int) (paperFormat.width() / 25.4 * dpi);
int imageHeight = (int) (paperFormat.height() / 25.4 * dpi);
drawImage(graphics2d, bounds, imageWidth, imageHeight, 0);
graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (shapesFile != null && shapesFile.exists()) {
applyShapes(graphics2d);
}
if (legend != null) {
graphics2d.drawImage(legend, null, legendX, legendY);
}
if (scalePrefix != null) {
Font scaleFont = graphics2d.getFont().deriveFont(scaleSize);
graphics2d.setFont(scaleFont);
FontMetrics fontMetrics = graphics2d.getFontMetrics(scaleFont);
String scaleString = scalePrefix + "1:" + (int) scale;
Rectangle2D stringBounds = fontMetrics.getStringBounds(scaleString, graphics2d);
double width = stringBounds.getWidth();
double height = stringBounds.getHeight();
graphics2d.setColor(Color.white);
double border = 5;
graphics2d.fillRect((int) scaleX, (int) (scaleY - height + 2 * border), (int) (width + 3 * border),
(int) (height + 2 * border));
graphics2d.setColor(Color.black);
graphics2d.drawString(scaleString, (int) scaleX + 5, (int) scaleY);
}
}
private void applyShapes( Graphics2D graphics ) throws Exception {
Stream<String> lines = Files.lines(Paths.get(shapesFile.toURI())).distinct()//
.filter(l -> l.trim().length() != 0);
lines.forEach(l -> {
if (l.startsWith("text")) {
// text;x;y;mytext;colorrgba;size
String[] split = l.split(";");
int x = Integer.parseInt(split[1]);
int y = Integer.parseInt(split[2]);
String msg = split[3];
Color color = ColorUtilities.colorFromRbgString(split[4]);
int size = Integer.parseInt(split[5]);
graphics.setColor(color);
graphics.setFont(new Font("Arial", Font.PLAIN, size));
graphics.drawString(msg, x, y);
} else if (l.startsWith("box")) {
// box;x;y;w;h;strokewidth;fillrgba;strokergba
String[] split = l.split(";");
int x = Integer.parseInt(split[1]);
int y = Integer.parseInt(split[2]);
int w = Integer.parseInt(split[3]);
int h = Integer.parseInt(split[4]);
int strokeWidth = Integer.parseInt(split[5]);
Color colorFill = ColorUtilities.colorFromRbgString(split[6]);
Color colorStroke = ColorUtilities.colorFromRbgString(split[7]);
graphics.setColor(colorFill);
graphics.fillRect(x, y, w, h);
BasicStroke stroke = new BasicStroke(strokeWidth);
graphics.setStroke(stroke);
graphics.setColor(colorStroke);
graphics.drawRect(x, y, w, h);
} else if (l.startsWith("roundedbox")) {
// roundedbox;x;y;w;h;round;strokewidth;fillrgba;strokergba
String[] split = l.split(";");
int x = Integer.parseInt(split[1]);
int y = Integer.parseInt(split[2]);
int w = Integer.parseInt(split[3]);
int h = Integer.parseInt(split[4]);
int round = Integer.parseInt(split[5]);
int strokeWidth = Integer.parseInt(split[6]);
Color colorFill = ColorUtilities.colorFromRbgString(split[7]);
Color colorStroke = ColorUtilities.colorFromRbgString(split[8]);
graphics.setColor(colorFill);
graphics.fillRoundRect(x, y, w, h, round, round);
BasicStroke stroke = new BasicStroke(strokeWidth);
graphics.setStroke(stroke);
graphics.setColor(colorStroke);
graphics.drawRoundRect(x, y, w, h, round, round);
}
});
}
private Style getGrassStyle( String path ) throws Exception {
List<String> valuesList = new ArrayList<String>();
List<Color> colorsList = new ArrayList<Color>();
StyleBuilder sB = new StyleBuilder(sf);
RasterSymbolizer rasterSym = sf.createRasterSymbolizer();
File grassFile = new File(path);
String mapName = grassFile.getName();
String mapsetPath = grassFile.getParentFile().getParent();
GrassColorTable ctable = new GrassColorTable(mapsetPath, mapName, null);
Enumeration<ColorRule> rules = ctable.getColorRules();
while( rules.hasMoreElements() ) {
ColorRule element = (ColorRule) rules.nextElement();
float fromValue = element.getLowCategoryValue();
float toValue = element.getLowCategoryValue() + element.getCategoryRange();
byte[] lowcatcol = element.getColor(fromValue);
byte[] highcatcol = element.getColor(toValue);
Color fromColor = new Color((int) (lowcatcol[0] & 0xff), (int) (lowcatcol[1] & 0xff), (int) (lowcatcol[2] & 0xff));
Color toColor = new Color((int) (highcatcol[0] & 0xff), (int) (highcatcol[1] & 0xff), (int) (highcatcol[2] & 0xff));
String from = String.valueOf(fromValue);
if (!valuesList.contains(from)) {
valuesList.add(from);
colorsList.add(fromColor);
}
String to = String.valueOf(toValue);
if (!valuesList.contains(to)) {
valuesList.add(to);
colorsList.add(toColor);
}
}
ColorMap colorMap = sf.createColorMap();
if (valuesList.size() > 1) {
for( int i = 0; i < valuesList.size(); i++ ) {
String fromValueStr = valuesList.get(i);
// String toValueStr = valuesList.get(i + 1);
Color fromColor = colorsList.get(i);
// Color toColor = colorsList.get(i + 1);
// double[] values = {Double.parseDouble(fromValueStr),
// Double.parseDouble(toValueStr)};
// double opacity = 1.0;
Expression fromColorExpr = sB
.colorExpression(new java.awt.Color(fromColor.getRed(), fromColor.getGreen(), fromColor.getBlue(), 255));
// Expression toColorExpr = sB.colorExpression(new java.awt.Color(toColor.getRed(),
// toColor.getGreen(), toColor
// .getBlue(), 255));
Expression fromExpr = sB.literalExpression(Double.parseDouble(fromValueStr));
// Expression toExpr = sB.literalExpression(values[1]);
// Expression opacityExpr = sB.literalExpression(opacity);
ColorMapEntry entry = sf.createColorMapEntry();
entry.setQuantity(fromExpr);
entry.setColor(fromColorExpr);
// entry.setOpacity(opacityExpr);
colorMap.addColorMapEntry(entry);
// entry = sf.createColorMapEntry();
// entry.setQuantity(toExpr);
// entry.setOpacity(opacityExpr);
// entry.setColor(toColorExpr);
// colorMap.addColorMapEntry(entry);
}
} else if (valuesList.size() == 1) {
String fromValueStr = valuesList.get(0);
Color fromColor = colorsList.get(0);
// double opacity = 1.0;
Expression fromColorExpr = sB
.colorExpression(new java.awt.Color(fromColor.getRed(), fromColor.getGreen(), fromColor.getBlue(), 255));
Expression fromExpr = sB.literalExpression(Double.parseDouble(fromValueStr));
// Expression opacityExpr = sB.literalExpression(opacity);
ColorMapEntry entry = sf.createColorMapEntry();
entry.setQuantity(fromExpr);
entry.setColor(fromColorExpr);
// entry.setOpacity(opacityExpr);
colorMap.addColorMapEntry(entry);
colorMap.addColorMapEntry(entry);
} else {
throw new IllegalArgumentException();
}
rasterSym.setColorMap(colorMap);
/*
* set global transparency for the map
*/
int alpha = ctable.getAlpha();
rasterSym.setOpacity(sB.literalExpression(alpha / 255.0));
Style newStyle = SLD.wrapSymbolizers(rasterSym);
return newStyle;
}
}