package nl.wldelft.fews.system.plugin.dataImport; import com.sun.media.jai.codec.ByteArraySeekableStream; import com.sun.media.jai.codec.SeekableStream; import nl.wldelft.util.ColorUtils; import nl.wldelft.util.FileUtils; import nl.wldelft.util.IOUtils; import nl.wldelft.util.coverage.Geometry; import nl.wldelft.util.coverage.RegularGridGeometry; import nl.wldelft.util.geodatum.GeoDatum; import nl.wldelft.util.geodatum.GeoPoint; import nl.wldelft.util.io.BinaryParser; import nl.wldelft.util.io.VirtualInputDir; import nl.wldelft.util.io.VirtualInputDirConsumer; import nl.wldelft.util.timeseries.DefaultTimeSeriesHeader; import nl.wldelft.util.timeseries.TimeSeriesContentHandler; import javax.media.jai.JAI; import javax.media.jai.RenderedOp; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.awt.image.renderable.ParameterBlock; import java.io.BufferedInputStream; import java.io.File; import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; public class GrayscaleImageTimeSeriesParser implements BinaryParser<TimeSeriesContentHandler>, VirtualInputDirConsumer, FileFilter { private BufferedImage bufferedImage = null; private Graphics2D graphics = null; private int[] rgbs = null; private float[] values = null; private VirtualInputDir virtualInputDir = null; private String virtualFileName = null; private TimeSeriesContentHandler contentHandler = null; @Override public boolean accept(File pathname) { return getWorldFileExt(FileUtils.getFileExt(pathname)) != null; } @Override public void setVirtualInputDir(VirtualInputDir virtualInputDir) { this.virtualInputDir = virtualInputDir; } @Override public void parse(BufferedInputStream inputStream, String virtualFileName, TimeSeriesContentHandler contentHandler) throws Exception { this.virtualFileName = virtualFileName; this.contentHandler = contentHandler; DefaultTimeSeriesHeader header = new DefaultTimeSeriesHeader(); header.setParameterId("image"); header.setLocationId("image"); contentHandler.setNewTimeSeriesHeader(header); if (contentHandler.isCurrentTimeSeriesHeaderForAllTimesRejected()) return; contentHandler.setTime(getTime(new File(virtualFileName).getName(), contentHandler.getDefaultTimeZone())); if (contentHandler.isCurrentTimeSeriesHeaderForCurrentTimeRejected()) return; loadImage(inputStream, virtualFileName); Geometry geometry = loadGeometry(); contentHandler.setGeometry(geometry); contentHandler.setValueResolution(1.0f); contentHandler.setCoverageValues(values); contentHandler.applyCurrentFields(); } private Geometry loadGeometry() throws Exception { Geometry res = contentHandler.getOverrulingGeometry(); if (res == null) res = readWorldFile(); if (res.getCols() != bufferedImage.getWidth()) { throw new Exception("Width of image file (" + bufferedImage.getWidth() + ") differs from number of cols (" + res.getCols() + ") in grid required geometry or world file"); } if (res.getRows() != bufferedImage.getHeight()) { throw new Exception("Height of image file (" + bufferedImage.getHeight() + ") differs from number of rows (" + res.getRows() + ") in grid required geometry or world file"); } return res; } private void loadImage(InputStream inputStream, String fileName) throws Exception { String ext = FileUtils.getFileExt(fileName); String jaiFormatId = getJaiFormatId(ext); if (jaiFormatId == null) throw new Exception("Unsupported bitmap format " + inputStream); ParameterBlock parameterBlock = new ParameterBlock(); SeekableStream seekableStream = new ByteArraySeekableStream(IOUtils.readBytes(inputStream)); try { parameterBlock.add(seekableStream); RenderedOp image = JAI.create(jaiFormatId, parameterBlock); try { checkBuffers(image.getWidth(), image.getHeight()); graphics.clearRect(0, 0, image.getWidth(), image.getHeight()); graphics.drawImage(image.getAsBufferedImage(), 0, 0, null); for (int i = 0; i < values.length; i++) { int rgb = rgbs[i]; int a = rgb >>> 24 & 0xff; if (a == 0) { values[i] = Float.NaN; continue; } int r = rgb >>> 16 & 0xff; int g = rgb >>> 8 & 0xff; int b = rgb >>> 0 & 0xff; values[i] = (r + g + b) / 3; } } finally { image.dispose(); } } finally { seekableStream.close(); } } private void checkBuffers(int width, int height) { if (bufferedImage != null && bufferedImage.getWidth() == width && bufferedImage.getHeight() == height) return; bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); graphics = bufferedImage.createGraphics(); graphics.setBackground(ColorUtils.TRANSPARANT_COLOR); DataBufferInt dataBufferInt = (DataBufferInt) bufferedImage.getRaster().getDataBuffer(); rgbs = dataBufferInt.getData(); values = new float[width * height]; } public static String getWorldFileExt(String bitMapExt) { if (bitMapExt.equalsIgnoreCase("tif")) return "tfw"; if (bitMapExt.equalsIgnoreCase("gif")) return "gfw"; if (bitMapExt.equalsIgnoreCase("png")) return "pgw"; if (bitMapExt.equalsIgnoreCase("bmp")) return "bpw"; if (bitMapExt.equalsIgnoreCase("pcx")) return "pxw"; if (bitMapExt.equalsIgnoreCase("jpg")) return "jgw"; return null; } private static String getJaiFormatId(String bitmapExt) { if (bitmapExt.equalsIgnoreCase("tif")) return "tiff"; if (bitmapExt.equalsIgnoreCase("gif")) return "gif"; if (bitmapExt.equalsIgnoreCase("bmp")) return "bmp"; if (bitmapExt.equalsIgnoreCase("png")) return "png"; if (bitmapExt.equalsIgnoreCase("jpg")) return "jpeg"; return null; } private RegularGridGeometry readWorldFile() throws Exception { String worldFileExt = getWorldFileExt(FileUtils.getFileExt(virtualFileName)); String worldFile = FileUtils.getPathWithOtherExtension(virtualFileName, worldFileExt); if (!virtualInputDir.exists(worldFile)) throw new FileNotFoundException("Can not file world file " + worldFile); String[] values = IOUtils.readAllLines(virtualInputDir.getReader(worldFile)); double cellWidth = Double.parseDouble(values[0]); double cellHeight = -Double.parseDouble(values[3]); double x = Double.parseDouble(values[4]); double y = Double.parseDouble(values[5]); GeoDatum geoDatum = contentHandler.getDefaultGeoDatum(); GeoPoint bitMapUpperLeft = geoDatum.createXYZ(x, y, 0); return RegularGridGeometry.create(geoDatum, bitMapUpperLeft, cellWidth, cellHeight, bufferedImage.getHeight(), bufferedImage.getWidth()); } public static long getTime(String fileName, TimeZone timeZone) throws Exception { Pattern p = Pattern.compile("\\d{8}.\\d{4}"); Matcher matcher = p.matcher(fileName); boolean successfull = matcher.find(); if (!successfull) throw new Exception("File name should contain date time yyyyMMdd_HHmm " + fileName); String dateTimeText = matcher.group(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd" + dateTimeText.charAt(8) + "HHmm"); dateFormat.setTimeZone(timeZone); try { return dateFormat.parse(dateTimeText).getTime(); } catch (ParseException e) { throw new Exception("File name should contain valid date time yyyyMMdd_HHmm " + fileName); } } }