From 0d88df3c2e5bd0fbb608f44915d5111971b731c7 Mon Sep 17 00:00:00 2001 From: Frank Klitzing Date: Wed, 20 May 2026 13:41:46 +0200 Subject: [PATCH 1/3] feat: add new outputformat geotiff-cog --- .../JasperReportGeoTiffOutputFormat.java | 184 ++++++++++++++++++ .../mapfish-spring-config-output-formats.xml | 4 + 2 files changed, 188 insertions(+) create mode 100644 core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java diff --git a/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java b/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java new file mode 100644 index 0000000000..500a6e7836 --- /dev/null +++ b/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java @@ -0,0 +1,184 @@ +package org.mapfish.print.output; + +import static java.util.Map.entry; +import static org.mapfish.print.Constants.PDF_DPI; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Stroke; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.Map; +import net.sf.jasperreports.engine.*; +import org.geotools.api.coverage.grid.GridCoverageWriter; +import org.geotools.api.parameter.GeneralParameterValue; +import org.geotools.api.parameter.ParameterValueGroup; +import org.geotools.coverage.grid.GridCoverage2D; +import org.geotools.coverage.grid.GridCoverageFactory; +import org.geotools.coverage.grid.io.AbstractGridFormat; +import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams; +import org.geotools.gce.geotiff.GeoTiffFormat; +import org.geotools.gce.geotiff.GeoTiffWriteParams; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.referencing.CRS; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class JasperReportGeoTiffOutputFormat extends AbstractJasperReportOutputFormat + implements OutputFormat { + + private static final Logger LOGGER = + LoggerFactory.getLogger(JasperReportGeoTiffOutputFormat.class); + + public static final Map IMAGE_TYPES = + Map.ofEntries(entry("cog", BufferedImage.TYPE_4BYTE_ABGR)); + + private String fileSuffix; + + @Override + public String getContentType() { + return "image/" + this.fileSuffix; + } + + @Override + public String getFileSuffix() { + return this.fileSuffix; + } + + public void setFileSuffix(final String fileSuffix) { + this.fileSuffix = fileSuffix; + } + + @Override + protected void doExport(final OutputStream outputStream, final Print print) + throws JRException, IOException { + + JasperPrint jasperPrint = print.print(); + final int numPages = jasperPrint.getPages().size(); + + final float dpiRatio = (float) (print.dpi() / PDF_DPI); + final int pageHeightOnImage = (int) (jasperPrint.getPageHeight() * dpiRatio); + final int pageWidthOnImage = (int) (jasperPrint.getPageWidth() * dpiRatio); + final int separatorHeight = 1; + final int separatorHeightOnImage = (int) (separatorHeight * dpiRatio); + + final int imageType = IMAGE_TYPES.get(getFileSuffix().toLowerCase()); + BufferedImage reportImage = + new BufferedImage( + pageWidthOnImage, + numPages * pageHeightOnImage + (numPages - 1) * separatorHeightOnImage, + imageType); + Graphics2D graphics2D = reportImage.createGraphics(); + if (imageType != BufferedImage.TYPE_4BYTE_ABGR) { + graphics2D.setColor(Color.WHITE); + graphics2D.fillRect(0, 0, pageWidthOnImage, pageHeightOnImage); + } + + try { + JasperPrintManager printManager = JasperPrintManager.getInstance(print.context()); + + for (int pageIndex = 0; pageIndex < numPages; pageIndex++) { + Image pageImage = printManager.printToImage(jasperPrint, pageIndex, dpiRatio); + + graphics2D.drawImage( + pageImage, + 0, + (pageHeightOnImage + separatorHeight) * pageIndex, + pageWidthOnImage, + (pageHeightOnImage + separatorHeight) * pageIndex + pageHeightOnImage, + 0, + 0, + pageWidthOnImage, + pageHeightOnImage, + null); + } + + // draw separator line between the pages + final Stroke stroke = new BasicStroke(separatorHeightOnImage); + for (int pageIndex = 0; pageIndex < numPages - 1; pageIndex++) { + graphics2D.setColor(Color.black); + graphics2D.setStroke(stroke); + int y = (pageHeightOnImage + separatorHeight) * pageIndex + pageHeightOnImage; + graphics2D.drawLine(0, y, pageWidthOnImage, y); + } + } finally { + graphics2D.dispose(); + } + + writeCOG(reportImage, outputStream, print); + } + + private void writeCOG(BufferedImage image, OutputStream outputStream, Print print) + throws IOException { + + Map maps = + print.values().find(org.mapfish.print.attribute.map.MapAttribute.MapAttributeValues.class); + + if (maps.isEmpty()) { + throw new IllegalStateException("No map found in print values"); + } + + // Get the first map's bounds + org.mapfish.print.attribute.map.MapAttribute.MapAttributeValues mapValues = + maps.values().iterator().next(); + ReferencedEnvelope bbox = + mapValues + .getMapBounds() + .toReferencedEnvelope( + new java.awt.Rectangle(mapValues.getWidth(), mapValues.getHeight())); + + String srs = mapValues.getProjection(); + LOGGER.info("Map SRS: {}", srs); + + try { + + bbox.setCoordinateReferenceSystem(CRS.decode(srs)); + + GridCoverageFactory factory = new GridCoverageFactory(); + GridCoverage2D coverage = factory.create("coverage", image, bbox); + + final int tileWidth = 512; // fixeded value + final int tileHeight = 512; // fixeded value + + File tmp = File.createTempFile("cog-", ".tif"); + + // getting a format + final GeoTiffFormat format = new GeoTiffFormat(); + + // getting the write parameters + final GeoTiffWriteParams wp = new GeoTiffWriteParams(); + + // setting compression to LZW + wp.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT); + wp.setCompressionType("LZW"); + wp.setCompressionQuality(0.75F); + + // setting the tile size + wp.setTilingMode(GeoToolsWriteParams.MODE_EXPLICIT); + wp.setTiling(tileWidth, tileHeight); + + // setting the write parameters for this geotiff + final ParameterValueGroup params = format.getWriteParameters(); + params.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp); + + GridCoverageWriter writer = null; + + writer = format.getWriter(tmp); + writer.write( + coverage, + (GeneralParameterValue[]) params.values().toArray(new GeneralParameterValue[1])); + + Files.copy(tmp.toPath(), outputStream); + writer.dispose(); + tmp.delete(); + } catch (Exception e) { + LOGGER.error("Error writing GeoTIFF: ", e); + } + LOGGER.info("GeoTIFF written successfully"); + } +} diff --git a/core/src/main/resources/mapfish-spring-config-output-formats.xml b/core/src/main/resources/mapfish-spring-config-output-formats.xml index 91d75998f2..36bf249878 100644 --- a/core/src/main/resources/mapfish-spring-config-output-formats.xml +++ b/core/src/main/resources/mapfish-spring-config-output-formats.xml @@ -15,6 +15,8 @@ p:fileSuffix="tif"/> + + Date: Thu, 21 May 2026 11:36:33 +0200 Subject: [PATCH 2/3] fix: contentType and change file to path --- .../JasperReportGeoTiffOutputFormat.java | 27 +++++++++++-------- .../mapfish-spring-config-output-formats.xml | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java b/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java index 500a6e7836..5e5413d67d 100644 --- a/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java +++ b/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java @@ -9,10 +9,10 @@ import java.awt.Image; import java.awt.Stroke; import java.awt.image.BufferedImage; -import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; +import java.nio.file.Path; import java.util.Map; import net.sf.jasperreports.engine.*; import org.geotools.api.coverage.grid.GridCoverageWriter; @@ -135,6 +135,10 @@ private void writeCOG(BufferedImage image, OutputStream outputStream, Print prin String srs = mapValues.getProjection(); LOGGER.info("Map SRS: {}", srs); + GridCoverageWriter writer = null; + Path dir = null; + Path tmp = null; + try { bbox.setCoordinateReferenceSystem(CRS.decode(srs)); @@ -142,10 +146,11 @@ private void writeCOG(BufferedImage image, OutputStream outputStream, Print prin GridCoverageFactory factory = new GridCoverageFactory(); GridCoverage2D coverage = factory.create("coverage", image, bbox); - final int tileWidth = 512; // fixeded value - final int tileHeight = 512; // fixeded value + final int tileWidth = 512; // fixed value + final int tileHeight = 512; // fixed value - File tmp = File.createTempFile("cog-", ".tif"); + dir = Files.createTempDirectory("cog-print"); + tmp = Files.createTempFile(dir, "cog-", ".tif"); // getting a format final GeoTiffFormat format = new GeoTiffFormat(); @@ -166,19 +171,19 @@ private void writeCOG(BufferedImage image, OutputStream outputStream, Print prin final ParameterValueGroup params = format.getWriteParameters(); params.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp); - GridCoverageWriter writer = null; - - writer = format.getWriter(tmp); + writer = format.getWriter(tmp.toFile()); writer.write( coverage, (GeneralParameterValue[]) params.values().toArray(new GeneralParameterValue[1])); - Files.copy(tmp.toPath(), outputStream); - writer.dispose(); - tmp.delete(); + Files.copy(tmp, outputStream); + LOGGER.info("GeoTIFF written successfully"); } catch (Exception e) { LOGGER.error("Error writing GeoTIFF: ", e); + } finally { + writer.dispose(); + Files.delete(tmp); } - LOGGER.info("GeoTIFF written successfully"); + LOGGER.info("GeoTIFF writing process completed"); } } diff --git a/core/src/main/resources/mapfish-spring-config-output-formats.xml b/core/src/main/resources/mapfish-spring-config-output-formats.xml index 36bf249878..ce2d43533f 100644 --- a/core/src/main/resources/mapfish-spring-config-output-formats.xml +++ b/core/src/main/resources/mapfish-spring-config-output-formats.xml @@ -36,7 +36,7 @@ + p:fileSuffix="cog" p:contentType="image/tiff"/> Date: Thu, 4 Jun 2026 13:43:07 +0200 Subject: [PATCH 3/3] fix: cog print is a mapexport now --- .../JasperReportGeoTiffOutputFormat.java | 189 -------------- .../output/MapCogExportOutputFormat.java | 233 ++++++++++++++++++ .../mapfish-spring-config-output-formats.xml | 6 +- 3 files changed, 234 insertions(+), 194 deletions(-) delete mode 100644 core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java create mode 100644 core/src/main/java/org/mapfish/print/output/MapCogExportOutputFormat.java diff --git a/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java b/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java deleted file mode 100644 index 5e5413d67d..0000000000 --- a/core/src/main/java/org/mapfish/print/output/JasperReportGeoTiffOutputFormat.java +++ /dev/null @@ -1,189 +0,0 @@ -package org.mapfish.print.output; - -import static java.util.Map.entry; -import static org.mapfish.print.Constants.PDF_DPI; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Stroke; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; -import net.sf.jasperreports.engine.*; -import org.geotools.api.coverage.grid.GridCoverageWriter; -import org.geotools.api.parameter.GeneralParameterValue; -import org.geotools.api.parameter.ParameterValueGroup; -import org.geotools.coverage.grid.GridCoverage2D; -import org.geotools.coverage.grid.GridCoverageFactory; -import org.geotools.coverage.grid.io.AbstractGridFormat; -import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams; -import org.geotools.gce.geotiff.GeoTiffFormat; -import org.geotools.gce.geotiff.GeoTiffWriteParams; -import org.geotools.geometry.jts.ReferencedEnvelope; -import org.geotools.referencing.CRS; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class JasperReportGeoTiffOutputFormat extends AbstractJasperReportOutputFormat - implements OutputFormat { - - private static final Logger LOGGER = - LoggerFactory.getLogger(JasperReportGeoTiffOutputFormat.class); - - public static final Map IMAGE_TYPES = - Map.ofEntries(entry("cog", BufferedImage.TYPE_4BYTE_ABGR)); - - private String fileSuffix; - - @Override - public String getContentType() { - return "image/" + this.fileSuffix; - } - - @Override - public String getFileSuffix() { - return this.fileSuffix; - } - - public void setFileSuffix(final String fileSuffix) { - this.fileSuffix = fileSuffix; - } - - @Override - protected void doExport(final OutputStream outputStream, final Print print) - throws JRException, IOException { - - JasperPrint jasperPrint = print.print(); - final int numPages = jasperPrint.getPages().size(); - - final float dpiRatio = (float) (print.dpi() / PDF_DPI); - final int pageHeightOnImage = (int) (jasperPrint.getPageHeight() * dpiRatio); - final int pageWidthOnImage = (int) (jasperPrint.getPageWidth() * dpiRatio); - final int separatorHeight = 1; - final int separatorHeightOnImage = (int) (separatorHeight * dpiRatio); - - final int imageType = IMAGE_TYPES.get(getFileSuffix().toLowerCase()); - BufferedImage reportImage = - new BufferedImage( - pageWidthOnImage, - numPages * pageHeightOnImage + (numPages - 1) * separatorHeightOnImage, - imageType); - Graphics2D graphics2D = reportImage.createGraphics(); - if (imageType != BufferedImage.TYPE_4BYTE_ABGR) { - graphics2D.setColor(Color.WHITE); - graphics2D.fillRect(0, 0, pageWidthOnImage, pageHeightOnImage); - } - - try { - JasperPrintManager printManager = JasperPrintManager.getInstance(print.context()); - - for (int pageIndex = 0; pageIndex < numPages; pageIndex++) { - Image pageImage = printManager.printToImage(jasperPrint, pageIndex, dpiRatio); - - graphics2D.drawImage( - pageImage, - 0, - (pageHeightOnImage + separatorHeight) * pageIndex, - pageWidthOnImage, - (pageHeightOnImage + separatorHeight) * pageIndex + pageHeightOnImage, - 0, - 0, - pageWidthOnImage, - pageHeightOnImage, - null); - } - - // draw separator line between the pages - final Stroke stroke = new BasicStroke(separatorHeightOnImage); - for (int pageIndex = 0; pageIndex < numPages - 1; pageIndex++) { - graphics2D.setColor(Color.black); - graphics2D.setStroke(stroke); - int y = (pageHeightOnImage + separatorHeight) * pageIndex + pageHeightOnImage; - graphics2D.drawLine(0, y, pageWidthOnImage, y); - } - } finally { - graphics2D.dispose(); - } - - writeCOG(reportImage, outputStream, print); - } - - private void writeCOG(BufferedImage image, OutputStream outputStream, Print print) - throws IOException { - - Map maps = - print.values().find(org.mapfish.print.attribute.map.MapAttribute.MapAttributeValues.class); - - if (maps.isEmpty()) { - throw new IllegalStateException("No map found in print values"); - } - - // Get the first map's bounds - org.mapfish.print.attribute.map.MapAttribute.MapAttributeValues mapValues = - maps.values().iterator().next(); - ReferencedEnvelope bbox = - mapValues - .getMapBounds() - .toReferencedEnvelope( - new java.awt.Rectangle(mapValues.getWidth(), mapValues.getHeight())); - - String srs = mapValues.getProjection(); - LOGGER.info("Map SRS: {}", srs); - - GridCoverageWriter writer = null; - Path dir = null; - Path tmp = null; - - try { - - bbox.setCoordinateReferenceSystem(CRS.decode(srs)); - - GridCoverageFactory factory = new GridCoverageFactory(); - GridCoverage2D coverage = factory.create("coverage", image, bbox); - - final int tileWidth = 512; // fixed value - final int tileHeight = 512; // fixed value - - dir = Files.createTempDirectory("cog-print"); - tmp = Files.createTempFile(dir, "cog-", ".tif"); - - // getting a format - final GeoTiffFormat format = new GeoTiffFormat(); - - // getting the write parameters - final GeoTiffWriteParams wp = new GeoTiffWriteParams(); - - // setting compression to LZW - wp.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT); - wp.setCompressionType("LZW"); - wp.setCompressionQuality(0.75F); - - // setting the tile size - wp.setTilingMode(GeoToolsWriteParams.MODE_EXPLICIT); - wp.setTiling(tileWidth, tileHeight); - - // setting the write parameters for this geotiff - final ParameterValueGroup params = format.getWriteParameters(); - params.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp); - - writer = format.getWriter(tmp.toFile()); - writer.write( - coverage, - (GeneralParameterValue[]) params.values().toArray(new GeneralParameterValue[1])); - - Files.copy(tmp, outputStream); - LOGGER.info("GeoTIFF written successfully"); - } catch (Exception e) { - LOGGER.error("Error writing GeoTIFF: ", e); - } finally { - writer.dispose(); - Files.delete(tmp); - } - LOGGER.info("GeoTIFF writing process completed"); - } -} diff --git a/core/src/main/java/org/mapfish/print/output/MapCogExportOutputFormat.java b/core/src/main/java/org/mapfish/print/output/MapCogExportOutputFormat.java new file mode 100644 index 0000000000..ebfee325da --- /dev/null +++ b/core/src/main/java/org/mapfish/print/output/MapCogExportOutputFormat.java @@ -0,0 +1,233 @@ +package org.mapfish.print.output; + +import jakarta.annotation.Nonnull; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.imageio.ImageIO; +import org.geotools.api.coverage.grid.GridCoverageWriter; +import org.geotools.api.parameter.GeneralParameterValue; +import org.geotools.api.parameter.ParameterValueGroup; +import org.geotools.api.referencing.crs.CoordinateReferenceSystem; +import org.geotools.api.referencing.operation.MathTransform; +import org.geotools.coverage.grid.GridCoverage2D; +import org.geotools.coverage.grid.GridCoverageFactory; +import org.geotools.coverage.grid.io.AbstractGridFormat; +import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams; +import org.geotools.gce.geotiff.GeoTiffFormat; +import org.geotools.gce.geotiff.GeoTiffWriteParams; +import org.geotools.referencing.CRS; +import org.geotools.referencing.operation.transform.AffineTransform2D; +import org.mapfish.print.Constants; +import org.mapfish.print.config.Configuration; +import org.mapfish.print.config.Template; +import org.mapfish.print.http.MfClientHttpRequestFactoryImpl; +import org.mapfish.print.processor.Processor; +import org.mapfish.print.processor.ProcessorDependencyGraph; +import org.mapfish.print.processor.map.CreateMapProcessor; +import org.mapfish.print.wrapper.json.PJsonArray; +import org.mapfish.print.wrapper.json.PJsonObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +/** + * The MapCogExportOutputFormat class. + * + * @author Frank and Manuel + */ +public class MapCogExportOutputFormat implements OutputFormat { + + private static final double METERS_PER_INCH = Constants.INCH_TO_MM / 1000.0; + + private static final String MAP_SUBREPORT = "mapSubReport"; + + @Autowired private ForkJoinPool forkJoinPool; + + @Autowired private MfClientHttpRequestFactoryImpl httpRequestFactory; + + private String fileSuffix; + + private String contentType; + + @Value("${httpRequest.fetchRetry.maxNumber}") + private int httpRequestMaxNumberFetchRetry; + + @Value("${httpRequest.fetchRetry.intervalMillis}") + private int httpRequestFetchRetryIntervalMillis; + + @Override + public final String getContentType() { + return this.contentType; + } + + public final void setContentType(final String contentType) { + this.contentType = contentType; + } + + @Override + public final String getFileSuffix() { + return this.fileSuffix; + } + + public final void setFileSuffix(final String fileSuffix) { + this.fileSuffix = fileSuffix; + } + + private String getMapSubReportVariable(final Template template) { + for (Processor processor : template.getProcessors()) { + if (processor instanceof CreateMapProcessor) { + String mapSubReport = + ((CreateMapProcessor) processor).getOutputMapperBiMap().get(MAP_SUBREPORT); + return Objects.requireNonNullElse(mapSubReport, MAP_SUBREPORT); + } + } + // validation has already confirmed there is exactly one createmap processor + // so this cannot happen + return null; + } + + @Override + public final Processor.ExecutionContext print( + @Nonnull final Map mdcContext, + final PJsonObject spec, + final Configuration config, + final File configDir, + final File taskDirectory, + final OutputStream outputStream) + throws Exception { + final String templateName = spec.getString(Constants.JSON_LAYOUT_KEY); + + final Template template = config.getTemplate(templateName); + + final Values values = + new Values( + mdcContext, + spec, + template, + taskDirectory, + this.httpRequestFactory, + null, + "tif", + httpRequestMaxNumberFetchRetry, + httpRequestFetchRetryIntervalMillis, + new AtomicBoolean(false)); + + final ProcessorDependencyGraph.ProcessorGraphForkJoinTask task = + template.getProcessorGraph().createTask(values); + final ForkJoinTask taskFuture = this.forkJoinPool.submit(task); + + try { + taskFuture.get(); + } catch (InterruptedException exc) { + // if cancel() is called on the current thread, this exception will be thrown. + // in this case, also properly cancel the task future. + taskFuture.cancel(true); + Thread.currentThread().interrupt(); + throw new CancellationException(); + } + + String mapSubReport = values.getString(getMapSubReportVariable(template)); + + GridCoverageWriter writer = null; + Path dir = null; + Path tmp = null; + + try { + + final PJsonObject mapJson = spec.getJSONObject("attributes").getJSONObject("map"); + Path path = + mapSubReport.startsWith("file:") + ? Paths.get(new URI(mapSubReport)) + : Path.of(mapSubReport); + + BufferedImage image = ImageIO.read(path.toFile()); + PJsonArray center = mapJson.getJSONArray("center"); + + double centerX = center.getDouble(0); + double centerY = center.getDouble(1); + double cx = image.getWidth() / 2.0; + double cy = image.getHeight() / 2.0; + + double scale = mapJson.getDouble("scale"); + double dpi = mapJson.getDouble("dpi"); + double metersPerPixel = scale * METERS_PER_INCH / dpi; + String srs = mapJson.getString("projection"); + double rotation = mapJson.getDouble("rotation"); + + AffineTransform gridToCRS = new AffineTransform(); + gridToCRS.translate(centerX, centerY); + gridToCRS.rotate(Math.toRadians(rotation)); + gridToCRS.scale(metersPerPixel, -metersPerPixel); + gridToCRS.translate(-cx, -cy); + + CoordinateReferenceSystem crs = CRS.decode(srs); + MathTransform mathTransform = new AffineTransform2D(gridToCRS); + GridCoverageFactory factory = new GridCoverageFactory(); + GridCoverage2D coverage = + factory.create("coverage", image, crs, mathTransform, null, null, null); + + final int tileWidth = 512; + final int tileHeight = 512; + + dir = Files.createTempDirectory("cog-print"); + tmp = Files.createTempFile(dir, "cog-", ".tif"); + + // write the GridCoverage2D to a GeoTIFF file with LZW compression and tiling + // using GeoTools + final GeoTiffFormat format = new GeoTiffFormat(); + final GeoTiffWriteParams wp = new GeoTiffWriteParams(); + + wp.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT); + wp.setCompressionType("LZW"); + wp.setCompressionQuality(0.75F); + + wp.setTilingMode(GeoToolsWriteParams.MODE_EXPLICIT); + wp.setTiling(tileWidth, tileHeight); + + final ParameterValueGroup params = format.getWriteParameters(); + params.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp); + params.parameter(GeoTiffFormat.RETAIN_AXES_ORDER.getName().toString()).setValue(true); + + // write the coverage to the GeoTIFF file using the specified parameters + writer = format.getWriter(tmp.toFile()); + writer.write( + coverage, + (GeneralParameterValue[]) params.values().toArray(new GeneralParameterValue[0])); + + writer.dispose(); + + // copy the temporary file to the output stream + Files.copy(tmp, outputStream); + } catch (Exception e) { + throw new IOException("Error writing cog file", e); + } finally { + + if (writer != null) { + try { + writer.dispose(); + } catch (Exception e) { + /* ignore */ } + } + if (tmp != null) { + Files.deleteIfExists( + tmp); // will only succeed if the file is not locked by the writer anymore + } + if (dir != null) { + Files.deleteIfExists(dir); // will only succeed if the tmp file has already been deleted + } + } + return task.getExecutionContext(); + } +} diff --git a/core/src/main/resources/mapfish-spring-config-output-formats.xml b/core/src/main/resources/mapfish-spring-config-output-formats.xml index ce2d43533f..088594d9de 100644 --- a/core/src/main/resources/mapfish-spring-config-output-formats.xml +++ b/core/src/main/resources/mapfish-spring-config-output-formats.xml @@ -11,12 +11,8 @@ p:fileSuffix="jpg"/> - - -