/* 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 tms; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.nio.file.Path; import javax.activation.MimeType; import javax.activation.MimeTypeParseException; import javax.imageio.ImageWriteParam; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import com.rgi.common.BoundingBox; import com.rgi.common.coordinate.Coordinate; import com.rgi.common.coordinate.CoordinateReferenceSystem; import com.rgi.common.coordinate.CrsCoordinate; import com.rgi.common.coordinate.referencesystem.profile.CrsProfile; import com.rgi.common.coordinate.referencesystem.profile.CrsProfileFactory; import com.rgi.common.coordinate.referencesystem.profile.GlobalGeodeticCrsProfile; import com.rgi.common.coordinate.referencesystem.profile.SphericalMercatorCrsProfile; import com.rgi.common.tile.TileOrigin; import com.rgi.store.tiles.TileStoreException; import com.rgi.store.tiles.tms.TmsReader; import com.rgi.store.tiles.tms.TmsWriter; /** * @author Steven D. Lander * @author Luke D. Lambert * @author Jenifer Cochran * */ @SuppressWarnings("javadoc") public class TmsWriterTest { @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); /** * Tests if the tms reader returns the expected tile * @throws TileStoreException throws if a tileStoreException occurs * @throws MimeTypeParseException throws if the MimeType cannot be detected */ @Test public void verifyTileRetrieval() throws TileStoreException, MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 4); final int zoomLevel = 1; final Coordinate<Integer> coordinate = new Coordinate<>(0, 0); final CoordinateReferenceSystem coordinateReferenceSystem = new CoordinateReferenceSystem("EPSG", 3857); try(final TmsWriter tmsWriter = new TmsWriter(coordinateReferenceSystem, tmsDir, new MimeType("image", "png")); final TmsReader tmsReader = new TmsReader(coordinateReferenceSystem, tmsDir)) { final BufferedImage image = createImage(); tmsWriter.addTile(coordinate.getX(), coordinate.getY(), zoomLevel, image); final BufferedImage tileImage = tmsReader.getTile(coordinate.getX(), coordinate.getY(), zoomLevel); assertTrue(bufferedImagesEqual(image, tileImage)); } } @Test public void verifyTileInsertion() throws TileStoreException, MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 4); final int zoomLevel = 5; final Coordinate<Integer> coordinate = new Coordinate<>(0, 0); final CoordinateReferenceSystem coordinateReferenceSystem = new CoordinateReferenceSystem("EPSG", 3857); try(final TmsWriter tmsWriter = new TmsWriter(coordinateReferenceSystem, tmsDir, new MimeType("image", "png"))) { final Path tilePath = tmsDir.resolve(Integer.toString(zoomLevel)) .resolve(Integer.toString(coordinate.getX())) .resolve(Integer.toString(coordinate.getX()) + ".png"); final BufferedImage img = createImage(); tmsWriter.addTile(coordinate.getX(), coordinate.getY(), zoomLevel, img); assertTrue(tilePath.toFile().exists()); } } @Test public void verifyTileInsertion2() throws TileStoreException, MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 4); final int zoomLevel = 5; final Coordinate<Integer> coordinate = new Coordinate<>(0, 0); final CrsProfile mercator = CrsProfileFactory.create("EPSG", 3857); try(final TmsWriter tmsWriter = new TmsWriter(mercator.getCoordinateReferenceSystem(), tmsDir, new MimeType("image", "png"))) { final Path tilePath = tmsDir.resolve(Integer.toString(zoomLevel)) .resolve(Integer.toString(coordinate.getX())) .resolve(Integer.toString(coordinate.getX()) + ".png"); final BufferedImage img = createImage(); final CrsCoordinate crsCoordinate = new CrsCoordinate(mercator.getBounds().getMinimumX(), mercator.getBounds().getMinimumY(), mercator.getCoordinateReferenceSystem()); tmsWriter.addTile(crsCoordinate, zoomLevel, img); assertTrue(tilePath.toFile().exists()); } } /** * Tests if TmsWriter can return the expected boundingBox for a particular tile * @throws MimeTypeParseException */ @Test public void verifyTileBoundingBox() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 4326), tmsDir, new MimeType("image/png"))) { final int column = 3; final int row = 2; final int zoom = 3; final BoundingBox bBoxReturned = tmsWriter.getTileBoundingBox(column, row, zoom); final BoundingBox bBoxExpected = getExpectedBoundingBox(column, row, zoom, new GlobalGeodeticCrsProfile()); assertBoundingBoxesEqual(bBoxExpected, bBoxReturned); } } /** * Tests if TmsWriter can return the expected boundingBox for a particular tile * @throws MimeTypeParseException */ @Test public void verifyTileBoundingBox2() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 4); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 3857), tmsDir, new MimeType("image/png"))) { final int column = 15; final int row = 0; final int zoom = 4; final BoundingBox bBoxReturned = tmsWriter.getTileBoundingBox(column, row, zoom); final BoundingBox bBoxExpected = getExpectedBoundingBox(column, row, zoom, new SphericalMercatorCrsProfile()); assertBoundingBoxesEqual(bBoxExpected, bBoxReturned); } } /** * Tests if TmsWriter can return the expected boundingBox for a particular tile * @throws MimeTypeParseException */ @Test public void verifyTileBoundingBox3() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 2); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 3857), tmsDir, new MimeType("image/png"))) { final int column = 0; final int row = 0; final int zoom = 0; final BoundingBox bBoxReturned = tmsWriter.getTileBoundingBox(column, row, zoom); final BoundingBox bBoxExpected = getExpectedBoundingBox(column, row, zoom, new SphericalMercatorCrsProfile()); assertBoundingBoxesEqual(bBoxExpected, bBoxReturned); } } @Test public void verifyTileToCrsCoordinate() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 3857), tmsDir, new MimeType("image/png"))) { final int column = 2; final int row = 7; final int zoom = 3; final TileOrigin corner = TileOrigin.UpperRight; final CrsProfile crsProfile = new SphericalMercatorCrsProfile(); final CrsCoordinate crsCoordReturned = tmsWriter.tileToCrsCoordinate (column, row, zoom, corner); final CrsCoordinate crsCoordExpected = getExpectedCrsCoordinate((column + 1), (row + 1), zoom, crsProfile); //since it is upper right // add one to both column and row assertEquals(crsCoordExpected, crsCoordReturned); } } @Test public void verifyTileToCrsCoordinate2() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 3857), tmsDir, new MimeType("image/png"))) { final int column = 2; final int row = 7; final int zoom = 3; final TileOrigin corner = TileOrigin.UpperLeft; final CrsProfile crsProfile = new SphericalMercatorCrsProfile(); final CrsCoordinate crsCoordReturned = tmsWriter.tileToCrsCoordinate (column, row, zoom, corner); final CrsCoordinate crsCoordExpected = getExpectedCrsCoordinate(column, (row + 1), zoom, crsProfile); //since it is upper // add one to row assertEquals(crsCoordExpected, crsCoordReturned); } } @Test public void verifyTileToCrsCoordinate3() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 3857), tmsDir, new MimeType("image/png"))) { final int column = 2; final int row = 7; final int zoom = 3; final TileOrigin corner = TileOrigin.LowerRight; final CrsProfile crsProfile = new SphericalMercatorCrsProfile(); final CrsCoordinate crsCoordReturned = tmsWriter.tileToCrsCoordinate(column, row, zoom, corner); final CrsCoordinate crsCoordExpected = getExpectedCrsCoordinate((column + 1), row, zoom, crsProfile); //since it is right // add one to column assertEquals(crsCoordExpected, crsCoordReturned); } } @Test public void verifyTileToCrsCoordinate4() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 3857), tmsDir, new MimeType("image/png"))) { final int column = 2; final int row = 7; final int zoom = 3; final TileOrigin corner = TileOrigin.LowerLeft; final CrsProfile crsProfile = new SphericalMercatorCrsProfile(); final CrsCoordinate crsCoordReturned = tmsWriter.tileToCrsCoordinate(column, row, zoom, corner); final CrsCoordinate crsCoordExpected = getExpectedCrsCoordinate(column, row, zoom, crsProfile); assertEquals(crsCoordExpected, crsCoordReturned); } } @Test public void verifyCrsToTileCoordinate() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderMercator(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 3857), tmsDir, new MimeType("image/png"), new ImageWriteParam(null))) { final int zoom = 3; final Coordinate<Integer> tileCoordinateExpected = new Coordinate<>(3, 5); final CrsCoordinate crsCoordinate = tmsWriter.tileToCrsCoordinate(tileCoordinateExpected.getX(), tileCoordinateExpected.getY(), zoom, TmsWriter.Origin); final Coordinate<Integer> tileCoordinateReturned = tmsWriter.crsToTileCoordinate(crsCoordinate, zoom); assertEquals(tileCoordinateExpected, tileCoordinateReturned); } } @Test public void verifyCrsToTileCoordinate2() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 4326), tmsDir, new MimeType("image/png"), null)) { final int zoom = 2; final Coordinate<Integer> tileCoordinateExpected = new Coordinate<>(0, 1); final CrsCoordinate crsCoordinate = tmsWriter.tileToCrsCoordinate(tileCoordinateExpected.getX(), tileCoordinateExpected.getY(), zoom, tmsWriter.getTileOrigin()); final Coordinate<Integer> tileCoordinateReturned = tmsWriter.crsToTileCoordinate(crsCoordinate, zoom); assertEquals(tileCoordinateExpected, tileCoordinateReturned); } } @Test(expected = IllegalArgumentException.class) public void writerConstructorIllegalArgumentException() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); try(final TmsWriter writer = new TmsWriter(null, tmsDir, new MimeType("image/png"))) { fail("Expected an IllegalArgumentException when passing a null value for CrsProfle."); } } @SuppressWarnings({ "static-method" }) @Test(expected = IllegalArgumentException.class) public void writerConstructorIllegalArgumentException2() throws MimeTypeParseException { try(final TmsWriter writer = new TmsWriter(new CoordinateReferenceSystem("EPSG", 4326), null, new MimeType("image/png"))) { fail("Expected an IllegalArgumentException when passing a null value for location."); } } @Test(expected = RuntimeException.class) public void writerConstructorIllegalArgumentException3() throws MimeTypeParseException, IOException { final Path badPath = this.tempFolder.newFile().toPath(); try(final TmsWriter writer = new TmsWriter(new CoordinateReferenceSystem("EPSG", 4326), badPath, new MimeType("image/png"))) { fail("Expected an IllegalArgumentException when passing a path to a file for location."); } } @Test(expected = IllegalArgumentException.class) public void writerConstructorIllegalArgumentException4() { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); try(final TmsWriter writer = new TmsWriter(new CoordinateReferenceSystem("EPSG", 3857), tmsDir, null)) { fail("Expected an IllegalArgumentException when OutputFormat paramter is null."); } } @Test(expected = IllegalArgumentException.class) public void tileToCrsCoordinateIllegalArgumentException() throws MimeTypeParseException { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 4326), tmsDir, new MimeType("image/png"))) { tmsWriter.tileToCrsCoordinate(2, 3, 1, null); fail("Expected an IllegalArgumentException when passing a null value for TileOrigin corner."); } } @Test(expected = IllegalArgumentException.class) public void addTileIllegalArgumentException() throws MimeTypeParseException, TileStoreException { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 4326), tmsDir, new MimeType("image/png"))) { tmsWriter.addTile(null, 3, createImage()); fail("Expected an IllegalArgumentException when passing a null value for coordinate"); } } @Test(expected = IllegalArgumentException.class) public void addTileIllegalArgumentException2() throws MimeTypeParseException, TileStoreException { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); final CrsProfile crs = CrsProfileFactory.create("EPSG", 4326); try(final TmsWriter tmsWriter = new TmsWriter(crs.getCoordinateReferenceSystem(), tmsDir, new MimeType("image/png"))) { final CrsCoordinate crsCoordinate = new CrsCoordinate(getExpectedCrsCoordinate(0, 2, 3, crs), crs.getCoordinateReferenceSystem()); tmsWriter.addTile(crsCoordinate, 3, null); fail("Expected an IllegalArgumentException when passing a null value for image"); } } @Test(expected = IllegalArgumentException.class) public void addTileIllegalArgumentException3() throws MimeTypeParseException, TileStoreException { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 4326), tmsDir, new MimeType("image/png"))) { final CrsCoordinate crsCoordinate = new CrsCoordinate(getExpectedCrsCoordinate(0, 2, 3, new SphericalMercatorCrsProfile()), new SphericalMercatorCrsProfile().getCoordinateReferenceSystem()); tmsWriter.addTile(crsCoordinate, 3, createImage()); fail("Expected an IllegalArgumentException when passing CrsCoordinate with a different coordinateReferenceSystem than the TMS."); } } @Test(expected = IllegalArgumentException.class) public void addTileIllegalArgumentException4() throws MimeTypeParseException, TileStoreException { final Path tmsDir = TmsUtility.createTMSFolderGeodetic(this.tempFolder, 3); try(final TmsWriter tmsWriter = new TmsWriter(new CoordinateReferenceSystem("EPSG", 4326), tmsDir, new MimeType("image/png"))) { tmsWriter.addTile(0, 0, 3, null); fail("Expected an IllegalArgumentException when passing a null value for image"); } } private static BoundingBox getExpectedBoundingBox(final int column, final int row, final int zoom, final CrsProfile crsProfile) { final Coordinate<Double> lowerLeftCoord = new Coordinate<>(getExpectedCrsCoordinate(column, row, zoom, crsProfile)); final Coordinate<Double> upperRightCoord = new Coordinate<>(getExpectedCrsCoordinate((column + 1), (row + 1), zoom, crsProfile)); return new BoundingBox(lowerLeftCoord.getX(), lowerLeftCoord.getY(), upperRightCoord.getX(), upperRightCoord.getY()); } private static CrsCoordinate getExpectedCrsCoordinate(final int column, final int row, final int zoom, final CrsProfile crsProfile) { final BoundingBox bounds = crsProfile.getBounds(); final double crsTileWidth = bounds.getWidth() /Math.pow(2, zoom); final double crsTileHeight = bounds.getHeight()/Math.pow(2, zoom); final double xCoord = bounds.getMinimumX() + (column * crsTileWidth); final double yCoord = bounds.getMinimumY() + (row * crsTileHeight); return new CrsCoordinate(xCoord, yCoord, crsProfile.getCoordinateReferenceSystem()); } private static boolean boundingBoxesEqual(final BoundingBox bBoxExpected, final BoundingBox bBoxReturned) { final Coordinate<Double> lowerLeftExpected = bBoxExpected.getBottomLeft(); final Coordinate<Double> upperRightExpected = bBoxExpected.getTopRight(); final Coordinate<Double> lowerLeftReturned = bBoxReturned.getBottomLeft(); final Coordinate<Double> upperRightReturned = bBoxReturned.getTopRight(); return lowerLeftExpected.equals(lowerLeftReturned) && upperRightExpected.equals(upperRightReturned); } private static void assertBoundingBoxesEqual(final BoundingBox bBoxExpected, final BoundingBox bBoxReturned) { assertTrue(String.format("The BoundingBox returned is not the boundingBox expected.\nActual min/max x/y: (%f, %f, %f, %f)\nReturned min/max x/y: (%f, %f, %f, %f).", bBoxExpected.getMinimumX(), bBoxExpected.getMinimumY(), bBoxExpected.getMaximumX(), bBoxExpected.getMaximumY(), bBoxReturned.getMinimumX(), bBoxReturned.getMinimumY(), bBoxReturned.getMaximumX(), bBoxReturned.getMaximumY()), boundingBoxesEqual(bBoxExpected, bBoxReturned)); } private static BufferedImage createImage() { final BufferedImage img = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB); final Graphics2D graphics = img.createGraphics(); graphics.setPaint(new Color(255, 0, 0)); graphics.fillRect(0, 0, 256, 256); return img; } private static boolean bufferedImagesEqual(final BufferedImage img1, final BufferedImage img2) { if(img1.getWidth() != img2.getWidth() || img1.getHeight() != img2.getHeight()) { return false; } for(int x = 0; x < img1.getWidth(); x++) { for(int y = 0; y < img1.getHeight(); y++) { if(img1.getRGB(x, y) != img2.getRGB(x, y)) { return false; } } } return true; } }