package com.revolsys.elevation.gridded.esriascii; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import com.revolsys.collection.map.Maps; import com.revolsys.elevation.gridded.DoubleArrayGriddedElevationModel; import com.revolsys.elevation.gridded.GriddedElevationModel; import com.revolsys.geometry.cs.esri.EsriCoordinateSystems; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.io.BaseCloseable; import com.revolsys.io.FileUtil; import com.revolsys.io.Readers; import com.revolsys.properties.BaseObjectWithProperties; import com.revolsys.spring.resource.Resource; import com.revolsys.util.Exceptions; import com.revolsys.util.number.BigDecimals; import com.revolsys.util.number.Doubles; public class EsriAsciiGriddedElevationModelReader extends BaseObjectWithProperties implements BaseCloseable { private GeometryFactory geometryFactory = GeometryFactory.DEFAULT_3D; private final Resource resource; public EsriAsciiGriddedElevationModelReader(final Resource resource, final Map<String, ? extends Object> properties) { this.resource = resource; setProperties(properties); } protected BufferedReader getBufferedReader() { final String fileExtension = this.resource.getFileNameExtension(); try { if (fileExtension.equals("zip")) { final ZipInputStream in = this.resource.newBufferedInputStream(ZipInputStream::new); final String fileName = this.resource.getBaseName(); final String baseName = FileUtil.getBaseName(fileName); final String projName = baseName + ".prj"; for (ZipEntry zipEntry = in.getNextEntry(); zipEntry != null; zipEntry = in .getNextEntry()) { final String name = zipEntry.getName(); if (name.equals(projName)) { final String wkt = FileUtil.getString(new InputStreamReader(in, StandardCharsets.UTF_8), false); final GeometryFactory geometryFactory = EsriCoordinateSystems.getGeometryFactory(wkt); setGeometryFactory(geometryFactory); } else if (name.equals(fileName)) { return new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); } } throw new IllegalArgumentException("Cannot find " + fileName + " in " + this.resource); } else if (fileExtension.equals("gz")) { final InputStream in = this.resource.newBufferedInputStream(); final GZIPInputStream gzIn = new GZIPInputStream(in); return new BufferedReader(new InputStreamReader(gzIn, StandardCharsets.UTF_8)); } else { final GeometryFactory geometryFactory = EsriCoordinateSystems .getGeometryFactory(this.resource); setGeometryFactory(geometryFactory); return this.resource.newBufferedReader(); } } catch (final IOException e) { throw Exceptions.wrap("Unable to open: " + this.resource, e); } } public GeometryFactory getGeometryFactory() { return this.geometryFactory; } public GriddedElevationModel read() { try ( BufferedReader reader = getBufferedReader()) { double xCentre = Double.NaN; double yCentre = Double.NaN; double xCorner = Double.NaN; double yCorner = Double.NaN; double noDataValue = 0; int width = -1; int height = -1; int cellSize = 0; double elevation = Double.NaN; while (Double.isNaN(elevation)) { String keyword = Readers.readKeyword(reader); if (BigDecimals.isNumber(keyword)) { elevation = Doubles.toValid(keyword); } else { keyword = keyword.toLowerCase(); if ("ncols".equals(keyword)) { width = Readers.readInteger(reader); if (width <= 0) { throw new IllegalArgumentException("ncols must be > 0\n" + this.resource); } } else if ("nrows".equals(keyword)) { height = Readers.readInteger(reader); if (height <= 0) { throw new IllegalArgumentException("nrows must be > 0\n" + this.resource); } } else if ("cellsize".equals(keyword)) { cellSize = Readers.readInteger(reader); if (cellSize <= 0) { throw new IllegalArgumentException("cellsize must be > 0\n" + this.resource); } } else if ("xllcenter".equals(keyword)) { xCentre = Readers.readDouble(reader); } else if ("yllcenter".equals(keyword)) { yCentre = Readers.readDouble(reader); } else if ("xllcorner".equals(keyword)) { xCorner = Readers.readDouble(reader); } else if ("yllcorner".equals(keyword)) { yCorner = Readers.readDouble(reader); } else if ("nodata_value".equals(keyword)) { noDataValue = Readers.readDouble(reader); } else { // Skip unknown value Readers.readKeyword(reader); } } } double x; double y; if (width == 0) { throw new IllegalArgumentException("ncols not specified\n" + this.resource); } else if (height == 0) { throw new IllegalArgumentException("nrows not specified\n" + this.resource); } else if (cellSize == 0) { throw new IllegalArgumentException("cellsize not specified\n" + this.resource); } else if (Double.isNaN(xCentre)) { if (Double.isNaN(xCorner)) { throw new IllegalArgumentException( "xllcenter, yllcenter or xllcorner, yllcorner missing\n" + this.resource); } else { if (Double.isNaN(yCorner)) { throw new IllegalArgumentException( "xllcorner set must missing yllcorner\n" + this.resource); } else { x = xCorner; y = yCorner; } } } else { if (Double.isNaN(yCentre)) { throw new IllegalArgumentException( "xllcenter set must missing yllcenter\n" + this.resource); } else { x = xCentre - cellSize / 2.0; y = yCentre - cellSize / 2.0; } } final DoubleArrayGriddedElevationModel elevationModel = new DoubleArrayGriddedElevationModel( this.geometryFactory, x, y, width, height, cellSize); elevationModel.setResource(this.resource); if (Maps.getBool(getProperties(), EsriAsciiGriddedElevation.PROPERTY_READ_DATA, true)) { for (int gridY = height - 1; gridY >= 0; gridY--) { for (int gridX = 0; gridX < width; gridX++) { if (elevation == noDataValue) { elevationModel.setElevationNull(gridX, gridY); } else { elevationModel.setElevation(gridX, gridY, elevation); } elevation = Readers.readDouble(reader); } } } return elevationModel; } catch (final Throwable e) { throw Exceptions.wrap("Error reading: " + this.resource, e); } } public void setGeometryFactory(final GeometryFactory geometryFactory) { if (geometryFactory == null) { this.geometryFactory = GeometryFactory.DEFAULT_3D; } else { this.geometryFactory = geometryFactory; } } }