/* The MIT License (MIT) * * Copyright (c) 2015 Reinventing Geospatial, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.rgi.store.tiles.tms; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.NoSuchElementException; import java.util.Set; import javax.activation.MimeType; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; import com.rgi.common.coordinate.Coordinate; import com.rgi.common.coordinate.CoordinateReferenceSystem; import com.rgi.common.coordinate.CrsCoordinate; import com.rgi.common.util.MimeTypeUtility; import com.rgi.store.tiles.TileStoreException; import com.rgi.store.tiles.TileStoreWriter; /** * <a href="http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification">TMS</a> * implementation of {@link TileStoreWriter} * * @author Luke Lambert * */ public class TmsWriter extends TmsTileStore implements TileStoreWriter { /** * Constructor * * @param coordinateReferenceSystem * The coordinate reference system of this tile store * @param location * The location of this tile store on-disk * @param imageOutputFormat * Image format for used for output */ public TmsWriter(final CoordinateReferenceSystem coordinateReferenceSystem, final Path location, final MimeType imageOutputFormat) { this(coordinateReferenceSystem, location, imageOutputFormat, null); } /** * Constructor * * @param coordinateReferenceSystem * The coordinate reference system of this tile store * @param location * The location of this tile store on-disk * @param imageOutputFormat * Image format for used for output * @param imageWriteOptions * Controls details of the image writing process. If null, a default ImageWriteParam used instead */ public TmsWriter(final CoordinateReferenceSystem coordinateReferenceSystem, final Path location, final MimeType imageOutputFormat, final ImageWriteParam imageWriteOptions) { super(coordinateReferenceSystem, location); if(!location.toFile().canWrite()) { throw new IllegalArgumentException("Specified location cannot be written to"); } if(imageOutputFormat == null) { throw new IllegalArgumentException("Image output format may not be null"); } this.imageOutputFormat = imageOutputFormat; try { this.imageWriter = ImageIO.getImageWritersByMIMEType(imageOutputFormat.toString()).next(); } catch(final NoSuchElementException ex) { throw new IllegalArgumentException(String.format("Mime type '%s' is not a supported for image writing by your Java environment", imageOutputFormat.toString())); } this.imageWriteOptions = imageWriteOptions; } @Override public Coordinate<Integer> crsToTileCoordinate(final CrsCoordinate coordinate, final int zoomLevel) { return this.profile.crsToTileCoordinate(coordinate, this.profile.getBounds(), // TMS uses absolute tiling, which covers the whole globe this.tileScheme.dimensions(zoomLevel), TmsTileStore.Origin); } @Override public void addTile(final CrsCoordinate coordinate, final int zoomLevel, final BufferedImage image) throws TileStoreException { if(coordinate == null) { throw new IllegalArgumentException("Coordinate may not be null"); } if(image == null) { throw new IllegalArgumentException("Image may not be null"); } if(!coordinate.getCoordinateReferenceSystem().equals(this.profile.getCoordinateReferenceSystem())) { throw new IllegalArgumentException("Coordinate's coordinate reference system does not match the tile store's coordinate reference system"); } final Coordinate<Integer> tmsCoordinate = this.crsToTileCoordinate(coordinate, zoomLevel); this.addTile(tmsCoordinate.getX(), tmsCoordinate.getY(), zoomLevel, image); } @Override public void addTile(final int column, final int row, final int zoomLevel, final BufferedImage image) throws TileStoreException { if(image == null) { throw new IllegalArgumentException("Image may not be null"); } final Path tilePath = tmsPath(this.location, zoomLevel, column).resolve(String.format("%d.%s", row, this.imageOutputFormat.getSubType().toLowerCase())); try { final Path parentPath = tilePath.getParent(); if(parentPath == null) { throw new IllegalArgumentException(String.format("A parent directory does not exist for the tile z: %d, x: %d, y: %d.", zoomLevel, column, row)); } // Image will not write unless the directories exist leading to it. if(!parentPath.toFile().exists()) { final boolean directoryFound = (new File(parentPath.toString())).mkdirs(); if(!directoryFound) { throw new TileStoreException(String.format("Image directory does not exist. Invalid directory: %s", parentPath.toString())); } } try(final ImageOutputStream fileOutputStream = ImageIO.createImageOutputStream(tilePath.toFile())) { this.imageWriter.setOutput(fileOutputStream); try { this.imageWriter.write(null, new IIOImage(image, null, null), this.imageWriteOptions); } catch(final IOException ex) { if(this.imageWriteOptions == null || !this.imageWriteOptions.canWriteCompressed()) { throw ex; // If this isn't an issue caused by compression options being set, rethrow the exception } this.imageWriter.write(null, new IIOImage(image, null, null), null); } fileOutputStream.flush(); } } catch(final IOException ex) { throw new TileStoreException(ex); } } @Override public Set<MimeType> getSupportedImageFormats() { return TmsWriter.SupportedImageFormats; } private final MimeType imageOutputFormat; private final ImageWriter imageWriter; private final ImageWriteParam imageWriteOptions; /** * Image MimeTypes supported by the TMS tile store */ public static final Set<MimeType> SupportedImageFormats = MimeTypeUtility.createMimeTypeSet(ImageIO.getReaderMIMETypes()); }