// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.ImportImagePlugin; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.Properties; import java.util.Set; import java.util.Vector; import javax.imageio.ImageIO; import org.apache.log4j.Logger; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridCoverageFactory; import org.geotools.coverage.processing.CoverageProcessor; import org.geotools.data.DataSourceException; import org.geotools.data.WorldFileReader; import org.geotools.factory.Hints; import org.geotools.gce.geotiff.GeoTiffReader; import org.geotools.geometry.Envelope2D; import org.geotools.referencing.CRS; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.util.InternationalString; /** * Class provides methods for resampling operations, IO and stores important data. * * @author Christoph Beekmans, Fabian Kowitz, Anna Robaszkiewicz, Oliver Kuhn, Martin Ulitzny * */ public final class PluginOperations { private static final Logger logger = Logger.getLogger(PluginOperations.class); // contains descriptions of all available CRS static Vector<String> crsDescriptions; // the standard native CRS of user images static CoordinateReferenceSystem defaultSourceCRS; // description of 'defaultSourceCRS' static String defaultSourceCRSDescription; public enum SUPPORTEDIMAGETYPES { tiff, tif, jpg, jpeg, bmp, png } public enum POSTFIXES_WORLDFILE { wld, jgw, jpgw, pgw, pngw, tfw, tifw, bpw, bmpw, } private PluginOperations() { // Hide default constructor for utilities classes } /** * Reprojects a GridCoverage to a given CRS. */ public static GridCoverage2D reprojectCoverage(GridCoverage2D coverage, CoordinateReferenceSystem targetCrs) throws NoSuchAuthorityCodeException, FactoryException { // TODO: add category for NO_DATA values in coverage (transparency in image) GridCoverage2D destination = null; CoverageProcessor processor = new CoverageProcessor(); ParameterValueGroup resampleParams = processor.getOperation("Resample").getParameters(); // set parameters resampleParams.parameter("Source").setValue(coverage); resampleParams.parameter("CoordinateReferenceSystem").setValue(targetCrs); // resample coverage with given parameters destination = (GridCoverage2D) processor.doOperation(resampleParams); return destination; } /** * Creates a org.geotools.coverage.grid.GridCoverage2D from a given file. */ public static GridCoverage2D createGridFromFile(File file, CoordinateReferenceSystem refSys, boolean failIfNoPrjFile) throws IOException { GridCoverage2D coverage = null; if (!file.exists()) throw new FileNotFoundException("File not found."); String extension = null; String fileNameWithoutExt = null; int dotPos = file.getAbsolutePath().lastIndexOf("."); extension = file.getAbsolutePath().substring(dotPos); fileNameWithoutExt = file.getAbsolutePath().substring(0, dotPos); /*------- switch for file type -----------*/ if (extension.equalsIgnoreCase(".tif") || extension.equalsIgnoreCase(".tiff")) { // try to read GeoTIFF: try { coverage = readGeoTiff(file, refSys); return coverage; } catch (DataSourceException dse) { if (!dse.getMessage().contains("Coordinate Reference System is not available")) { dse.printStackTrace(); } } catch (FactoryException facte) { logger.fatal("Error while reading from GeoTIFF:", facte); throw new IOException(facte); } // file is no GeoTiff, searching for Worldfile and projection file: String[] postfixes = {"wld", "tfw", "tifw"}; // try to read Worldfile: WorldFileReader tfwReader = null; for (int i = 0; i < postfixes.length; i++) { File prjFile = new File(fileNameWithoutExt + "." + postfixes[i]); if (prjFile.exists()) { tfwReader = new WorldFileReader(prjFile); } } if (tfwReader == null) { throw new IOException("No Worldfile found."); } if (refSys == null) { // if no crs is delivered try to read projection file: refSys = readPrjFile(file); if (refSys == null) { if (failIfNoPrjFile) throw new IOException("No projection file found."); logger.debug("no projection given, no projection file found; using unprojected file."); } } BufferedImage img = ImageIO.read(file); if (img == null) { throw new IOException("Cannot read image file " + file.getAbsolutePath()); } // create Envelope double width = img.getWidth() * tfwReader.getXPixelSize(); double height = img.getHeight() * (-tfwReader.getYPixelSize()); double lowerLeft_x = tfwReader.getXULC(); double lowerLeft_y = tfwReader.getYULC() - height; Envelope2D bbox = new Envelope2D(null, new Rectangle2D.Double(lowerLeft_x, lowerLeft_y, width, height)); coverage = createGridCoverage(img, bbox, refSys); } else if (extension.equalsIgnoreCase(".jpg") || extension.equalsIgnoreCase(".jpeg")) { String[] postfixes = {"wld", "jgw", "jpgw"}; // try to read Worldfile: WorldFileReader tfwReader = null; for (int i = 0; i < postfixes.length; i++) { File prjFile = new File(fileNameWithoutExt + "." + postfixes[i]); if (prjFile.exists()) { tfwReader = new WorldFileReader(prjFile); } } if (tfwReader == null) throw new IOException("No Worldfile found."); if (refSys == null) { // if no crs is delivered try to read projection file: refSys = readPrjFile(file); if (refSys == null) { if (failIfNoPrjFile) throw new IOException("No projection file found."); logger.debug("no projection given, no projection file found; using unprojected file."); } } BufferedImage img = ImageIO.read(file); // create Envelope double width = img.getWidth() * tfwReader.getXPixelSize(); double height = img.getHeight() * (-tfwReader.getYPixelSize()); double lowerLeft_x = tfwReader.getXULC(); double lowerLeft_y = tfwReader.getYULC() - height; Envelope2D bbox = new Envelope2D(null, new Rectangle2D.Double(lowerLeft_x, lowerLeft_y, width, height)); coverage = createGridCoverage(img, bbox, refSys); } else if (extension.equalsIgnoreCase(".bmp")) { String[] postfixes = {"wld", "bmpw", "bpw"}; // try to read Worldfile: WorldFileReader tfwReader = null; for (int i = 0; i < postfixes.length; i++) { File prjFile = new File(fileNameWithoutExt + "." + postfixes[i]); if (prjFile.exists()) { tfwReader = new WorldFileReader(prjFile); } } if (tfwReader == null) throw new IOException("No Worldfile found."); if (refSys == null) { // if no crs is delivered try to read projection file: refSys = readPrjFile(file); if (refSys == null) { if (failIfNoPrjFile) throw new IOException("No projection file found."); logger.debug("no projection given, no projection file found; using unprojected file."); } } BufferedImage img = ImageIO.read(file); // create Envelope double width = img.getWidth() * tfwReader.getXPixelSize(); double height = img.getHeight() * (-tfwReader.getYPixelSize()); double lowerLeft_x = tfwReader.getXULC(); double lowerLeft_y = tfwReader.getYULC() - height; Envelope2D bbox = new Envelope2D(null, new Rectangle2D.Double(lowerLeft_x, lowerLeft_y, width, height)); coverage = createGridCoverage(img, bbox, refSys); } else if (extension.equalsIgnoreCase(".png")) { String[] postfixes = {"wld", "pgw", "pngw"}; // try to read Worldfile: WorldFileReader tfwReader = null; for (int i = 0; i < postfixes.length; i++) { File prjFile = new File(fileNameWithoutExt + "." + postfixes[i]); if (prjFile.exists()) { tfwReader = new WorldFileReader(prjFile); } } if (tfwReader == null) throw new IOException("No Worldfile found."); if (refSys == null) { // if no crs is delivered try to read projection file: refSys = readPrjFile(file); if (refSys == null) { if (failIfNoPrjFile) throw new IOException("No projection file found."); logger.debug("no projection given, no projection file found; using unprojected file."); } } BufferedImage img = ImageIO.read(file); // create Envelope double width = img.getWidth() * tfwReader.getXPixelSize(); double height = img.getHeight() * (-tfwReader.getYPixelSize()); double lowerLeft_x = tfwReader.getXULC(); double lowerLeft_y = tfwReader.getYULC() - height; Envelope2D bbox = new Envelope2D(null, new Rectangle2D.Double(lowerLeft_x, lowerLeft_y, width, height)); coverage = createGridCoverage(img, bbox, refSys); } else { throw new IOException("Image type not supported. Supported formats are: \n" + Arrays.toString(SUPPORTEDIMAGETYPES.values())); } return coverage; } /** * Searches for a projection file (.prj) with the same name of 'file' * tries to parse it. * * @param file image file, not the real world file (will be searched) */ public static CoordinateReferenceSystem readPrjFile(File file) throws IOException { CoordinateReferenceSystem refSys = null; String prjFilename = null; int dotPos = file.getAbsolutePath().lastIndexOf("."); prjFilename = file.getAbsolutePath().substring(0, dotPos) + ".prj"; File prjFile = new File(prjFilename); if (!prjFile.exists()) return null; logger.debug("Loading .prj file: " + prjFile.getAbsolutePath()); try (BufferedReader br = new BufferedReader(new FileReader(prjFile))) { StringBuilder sb = new StringBuilder(); String content = null; while ((content = br.readLine()) != null) { sb.append(content); } refSys = CRS.parseWKT(sb.toString().trim()); } catch (FactoryException e) { throw new IOException("Unable to parse prj-file: '" + prjFile.getName() + "'"); } return refSys; } /** * Method for external use. */ public static GridCoverage2D createGridCoverage(BufferedImage img, Envelope2D bbox, CoordinateReferenceSystem crs) { bbox.setCoordinateReferenceSystem(crs); return new GridCoverageFactory().create("", img, bbox); } /** * Method for reading a GeoTIFF file. * * @param refSys if delivered, the coverage will be forced to use this crs */ public static GridCoverage2D readGeoTiff(File file, CoordinateReferenceSystem refSys) throws IOException, FactoryException { GridCoverage2D coverage = null; Hints hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, true); if (refSys != null) { hints.put(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM, refSys); } // don't use the EPSG-Factory because of wrong behaviour //hints.put(Hints.CRS_AUTHORITY_FACTORY, CRS.getAuthorityFactory(true)); GeoTiffReader reader = new GeoTiffReader(file, hints); coverage = reader.read(null); return coverage; } /** * Loads CRS data from an EPSG database and creates descriptions for each one. */ public static void loadCRSData(Properties pluginProps) { String defaultcrsString = pluginProps.getProperty("default_crs_srid"); crsDescriptions = new Vector<>(); Set<String> supportedCodes = CRS.getSupportedCodes("EPSG"); CRSAuthorityFactory fac = CRS.getAuthorityFactory(false); for (Iterator<String> iterator = supportedCodes.iterator(); iterator.hasNext();) { String string = iterator.next(); try { if ("WGS84(DD)".equals(string)) { continue; } InternationalString desc = fac.getDescriptionText("EPSG:" + string); String description = desc.toString() + " [-EPSG:" + string + "-]"; crsDescriptions.add(description); if (defaultcrsString != null && defaultcrsString.equalsIgnoreCase("EPSG:" + string)) { boolean isEastingFirst = Boolean.valueOf(pluginProps.getProperty("default_crs_eastingfirst")); defaultSourceCRS = CRS.decode("EPSG:" + string, isEastingFirst); defaultSourceCRSDescription = description; } } catch (FactoryException e) { logger.error("Error while loading EPSG data: " + e.getMessage()); } } } }