/*
* (c) 1998-2016 University Corporation for Atmospheric Research/Unidata
*/
package thredds.server.ncss.view.gridaspoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import thredds.server.ncss.util.NcssRequestUtils;
import thredds.util.ContentType;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Attribute;
import ucar.nc2.constants.CDM;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.dt.GridCoordSystem;
import ucar.nc2.dt.GridDataset;
import ucar.nc2.dt.GridDatatype;
import ucar.nc2.dt.grid.GridAsPointDataset;
import ucar.nc2.time.CalendarDate;
import ucar.unidata.geoloc.LatLonPoint;
import ucar.unidata.geoloc.vertical.VerticalTransform;
class CSVPointDataWriter implements PointDataWriter {
static private Logger log = LoggerFactory.getLogger(CSVPointDataWriter.class);
protected PrintStream printWriter;
private Map<String, List<String>> allVars;
private Map<String, GridAsPointDataset> gridAsPointDatasets = new HashMap<>(); // LOOK WTF ??
private boolean headersSet = false;
private HttpHeaders httpHeaders;
protected CSVPointDataWriter(OutputStream os) {
try {
printWriter = new PrintStream(os, false, CDM.UTF8);
} catch (UnsupportedEncodingException e) {
log.error("CSVPointDataWriter", e);
}
}
public static CSVPointDataWriter factory(OutputStream os) {
return new CSVPointDataWriter(os);
}
public boolean header(Map<String, List<String>> groupedVars, GridDataset gridDataset, List<CalendarDate> wDates, List<Attribute> timeDimAtts, LatLonPoint point, Double vertCoord) {
allVars = groupedVars;
for (Entry<String, List<String>> entry : groupedVars.entrySet()) {
gridAsPointDatasets.put(entry.getKey(), NcssRequestUtils.buildGridAsPointDataset(gridDataset, entry.getValue()));
}
return true;
}
/*
*
* We write one header for each variables group so in this case iterate
* over variable group and over time for each group
*
* (non-Javadoc)
* @see thredds.server.ncSubset.view.PointDataWriter#write(java.util.Map, ucar.nc2.dt.GridDataset, java.util.List, ucar.unidata.geoloc.LatLonPoint, java.lang.Double)
*/
public boolean write(Map<String, List<String>> groupedVars, GridDataset gds, List<CalendarDate> wDates, LatLonPoint point, Double vertCoord) throws InvalidRangeException {
boolean allDone = true;
List<String> keys = new ArrayList<>(groupedVars.keySet());
//loop over variable groups
int contKeys = 1;
for (String key : keys) {
List<String> varsGroup = groupedVars.get(key);
boolean hasEnsembleDim = gds.findGridByShortName(varsGroup.get(0)).getEnsembleDimension() != null;
writeGroupHeader(varsGroup, gds, hasEnsembleDim, !wDates.isEmpty());
boolean pointRead = true;
if (wDates.isEmpty()) {
//pointRead = write(varsGroup, gds, point, vertCoord);
pointRead = write(key, gds, point, vertCoord);
} else {
//Loop over time
CalendarDate date;
Iterator<CalendarDate> it = wDates.iterator();
while (pointRead && it.hasNext()) {
date = it.next();
//pointRead = write(varsGroup, gds, date, point, vertCoord);
pointRead = write(key, gds, date, point, vertCoord);
}
}
if (contKeys != keys.size())
printWriter.println();
contKeys++;
}
return allDone;
}
/*
* Write method for datasets without time dimension
*/
private boolean write(String keyVarsGroup, GridDataset gridDataset, LatLonPoint point, Double targetLevel) {
boolean allDone = true;
List<String> varsGroup = allVars.get(keyVarsGroup);
GridAsPointDataset gap = gridAsPointDatasets.get(keyVarsGroup);
CoordinateAxis1D verticalAxisForGroup = gridDataset.findGridDatatype(varsGroup.get(0)).getCoordinateSystem().getVerticalAxis();
if (verticalAxisForGroup == null) {
//Read and write vars--> point
allDone = allDone && write(varsGroup, gridDataset, gap, point);
} else {
//read and write time, verCoord for each variable in group
if (targetLevel != null) {
Double vertCoord = NcssRequestUtils.getTargetLevelForVertCoord(verticalAxisForGroup, targetLevel);
allDone = write(varsGroup, gridDataset, gap, point, vertCoord, verticalAxisForGroup.getUnitsString());
} else {//All levels
for (Double vertCoord : verticalAxisForGroup.getCoordValues()) {
/////Fix axis!!!!
if (verticalAxisForGroup.getCoordValues().length == 1)
vertCoord = NcssRequestUtils.getTargetLevelForVertCoord(verticalAxisForGroup, vertCoord);
allDone = allDone && write(varsGroup, gridDataset, gap, point, vertCoord, verticalAxisForGroup.getUnitsString());
}
}
}
//printWriter.println();
//}
return allDone;
}
/*
* write method for grids with time dimension
*/
private boolean write(String keyVarsGroup, GridDataset gridDataset, CalendarDate date, LatLonPoint point, Double targetLevel) throws InvalidRangeException {
boolean allDone = true;
List<String> varsGroup = allVars.get(keyVarsGroup);
GridAsPointDataset gap = gridAsPointDatasets.get(keyVarsGroup);
CoordinateAxis1D verticalAxisForGroup = gridDataset.findGridDatatype(varsGroup.get(0)).getCoordinateSystem().getVerticalAxis();
//Ensemble handling...
GridDatatype refGrid = gridDataset.findGridDatatype(varsGroup.get(0));
CoordinateAxis1D ensembleAxisForGroup = refGrid.getCoordinateSystem().getEnsembleAxis();
double[] ensCoords = new double[]{-1};
if (ensembleAxisForGroup != null) {
ensCoords = ensembleAxisForGroup.getCoordValues();
}
for (double ensCoord : ensCoords) {
if (verticalAxisForGroup == null) {
//Read and write vars--> time, point
allDone = allDone && write(varsGroup, gridDataset, gap, date, point, ensCoord);
} else {
//read and write [ensCoord], time, verCoord for each variable in group
if (targetLevel != null) {
Double vertCoord = NcssRequestUtils.getTargetLevelForVertCoord(verticalAxisForGroup, targetLevel);
allDone = write(varsGroup, gridDataset, gap, date, point, ensCoord, vertCoord, verticalAxisForGroup.getUnitsString());
} else {//All levels
for (Double vertCoord : verticalAxisForGroup.getCoordValues()) {
/////Fix axis!!!!
if (verticalAxisForGroup.getCoordValues().length == 1)
vertCoord = NcssRequestUtils.getTargetLevelForVertCoord(verticalAxisForGroup, vertCoord);
allDone = allDone && write(varsGroup, gridDataset, gap, date, point, ensCoord, vertCoord, verticalAxisForGroup.getUnitsString());
}
}
}
}
return allDone;
}
protected void writeGroupHeader(List<String> varGroup, GridDataset gridDataset, boolean hasEnsAxis, boolean hasTimeAxis) {
StringBuilder sb = new StringBuilder();
if (hasTimeAxis)
sb.append("time,");
sb.append("latitude[unit=\"degrees_north\"],");
sb.append("longitude[unit=\"degrees_east\"],");
if (hasEnsAxis)
sb.append("ensMember,");
GridCoordSystem coordSystem = gridDataset.findGridDatatype(varGroup.get(0)).getCoordinateSystem();
CoordinateAxis1D zAxis = coordSystem.getVerticalAxis();
if (zAxis != null)
sb.append("vertCoord[unit=\"").append(zAxis.getUnitsString()).append("\"],");
VerticalTransform vt = coordSystem.getVerticalTransform();
if (vt != null)
sb.append("vertCoord[unit=\"").append(vt.getUnitString()).append("\"],");
Iterator<String> it = varGroup.iterator();
while (it.hasNext()) {
GridDatatype grid = gridDataset.findGridDatatype(it.next());
sb.append(grid.getName());
if (grid.getUnitsString() != null)
sb.append("[unit=\"").append(grid.getUnitsString()).append("\"]");
if (it.hasNext()) sb.append(",");
}
printWriter.print(sb.toString());
printWriter.println();
}
private boolean write(List<String> vars, GridDataset gridDataset, GridAsPointDataset gap, LatLonPoint point, Double targetLevel, String zUnits) {
boolean allDone = false;
int contVars = 0;
Iterator<String> itVars = vars.iterator();
try {
while (itVars.hasNext()) {
GridDatatype grid = gridDataset.findGridDatatype(itVars.next());
if (gap.hasVert(grid, targetLevel)) {
GridAsPointDataset.Point p = gap.readData(grid, null, targetLevel, point.getLatitude(), point.getLongitude());
if (contVars == 0) {
printWriter.print(point.getLatitude());
printWriter.print(",");
printWriter.print(point.getLongitude());
printWriter.print(",");
printWriter.print(p.z);
printWriter.print(",");
}
printWriter.print(p.dataValue);
if (itVars.hasNext()) printWriter.print(",");
} else {
// write missingvalues!!!
if (contVars == 0) {
printWriter.print(point.getLatitude());
printWriter.print(",");
printWriter.print(point.getLongitude());
printWriter.print(",");
printWriter.print(targetLevel);
printWriter.print(",");
}
printWriter.print(gap.getMissingValue(grid));
if (itVars.hasNext()) printWriter.print(",");
}
contVars++;
}
allDone = true;
} catch (IOException ioe) {
log.error("Error reading data", ioe);
}
printWriter.println();
return allDone;
}
private boolean write(List<String> vars, GridDataset gridDataset, GridAsPointDataset gap, CalendarDate date, LatLonPoint point, Double ensCoord, Double targetLevel, String zUnits) throws InvalidRangeException {
boolean allDone = false;
printWriter.print(date.toString());
printWriter.print(",");
int contVars = 0;
Iterator<String> itVars = vars.iterator();
try {
while (itVars.hasNext()) {
GridDatatype grid = gridDataset.findGridDatatype(itVars.next());
double actualLevel = NcssRequestUtils.getActualVertLevel(grid, date, point, targetLevel);
if (gap.hasTime(grid, date) && gap.hasVert(grid, targetLevel)) {
GridAsPointDataset.Point p = gap.readData(grid, date, ensCoord, targetLevel, point.getLatitude(), point.getLongitude());
if (contVars == 0) {
printWriter.print(point.getLatitude());
printWriter.print(",");
printWriter.print(point.getLongitude());
printWriter.print(",");
if (ensCoord >= 0) {
printWriter.print(p.ens);
printWriter.print(",");
}
printWriter.print(p.z);
printWriter.print(",");
if (Double.compare(actualLevel, -9999.9) != 0) { //Print the actual level LOOK WTF ??
printWriter.print(actualLevel);
printWriter.print(",");
}
}
printWriter.print(p.dataValue);
if (itVars.hasNext()) printWriter.print(",");
} else {
// write missingvalues!!!
if (contVars == 0) {
printWriter.print(point.getLatitude());
printWriter.print(",");
printWriter.print(point.getLongitude());
printWriter.print(",");
printWriter.print(targetLevel);
}
printWriter.print(gap.getMissingValue(grid));
if (itVars.hasNext()) printWriter.print(",");
}
contVars++;
}
allDone = true;
} catch (IOException ioe) {
log.error("Error reading data", ioe);
}
printWriter.println();
return allDone;
}
/*
* Write method for grids without time and vertical dimensions
*/
private boolean write(List<String> vars, GridDataset gridDataset, GridAsPointDataset gap, LatLonPoint point) {
boolean allDone = false;
int contVars = 0;
Iterator<String> itVars = vars.iterator();
try {
while (itVars.hasNext()) {
GridDatatype grid = gridDataset.findGridDatatype(itVars.next());
//if (gap.hasTime(grid, date) ) {
GridAsPointDataset.Point p = gap.readData(grid, null, point.getLatitude(), point.getLongitude());
if (contVars == 0) {
printWriter.print(point.getLatitude());
printWriter.print(",");
printWriter.print(point.getLongitude());
printWriter.print(",");
}
printWriter.print(p.dataValue);
if (itVars.hasNext()) printWriter.print(",");
//} else {
// write missingvalues!!!
// if(contVars==0){
// printWriter.write( point.getLatitude()+"," );
// printWriter.write( point.getLongitude() +"," );
// }
// printWriter.write( Double.valueOf(gap.getMissingValue(grid)).toString() );
//}
contVars++;
}
allDone = true;
} catch (IOException ioe) {
log.error("Error reading data", ioe);
}
printWriter.println();
return allDone;
}
private boolean write(List<String> vars, GridDataset gridDataset, GridAsPointDataset gap, CalendarDate date, LatLonPoint point, Double ensCoord) {
boolean allDone = false;
printWriter.print(date.toString());
printWriter.print(",");
int contVars = 0;
Iterator<String> itVars = vars.iterator();
try {
while (itVars.hasNext()) {
GridDatatype grid = gridDataset.findGridDatatype(itVars.next());
if (gap.hasTime(grid, date)) {
GridAsPointDataset.Point p = gap.readData(grid, date, ensCoord, -1, point.getLatitude(), point.getLongitude());
if (contVars == 0) {
printWriter.print(point.getLatitude());
printWriter.print(",");
printWriter.print(point.getLongitude());
printWriter.print(",");
if (ensCoord >= 0) {
printWriter.print(p.ens);
printWriter.print(",");
}
}
printWriter.print(p.dataValue);
if (itVars.hasNext()) printWriter.print(",");
} else {
// write missingvalues!!!
if (contVars == 0) {
printWriter.print(point.getLatitude());
printWriter.print(",");
printWriter.print(point.getLongitude());
printWriter.print(",");
}
printWriter.print(gap.getMissingValue(grid));
if (itVars.hasNext()) printWriter.print(",");
}
contVars++;
}
allDone = true;
} catch (IOException ioe) {
log.error("Error reading data", ioe);
}
printWriter.println();
return allDone;
}
@Override
public boolean trailer() {
printWriter.flush();
return true;
}
@Override
public HttpHeaders getResponseHeaders() {
return httpHeaders;
}
@Override
public void setHTTPHeaders(GridDataset gridDataset, String pathInfo, boolean isStream) {
if (!headersSet) {
httpHeaders = new HttpHeaders();
if (!isStream) {
httpHeaders.set("Content-Location", pathInfo);
String fileName = NcssRequestUtils.getFileNameForResponse(pathInfo, ".csv");
httpHeaders.set("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
}
httpHeaders.set(ContentType.HEADER, ContentType.csv.getContentHeader());
//httpHeaders.setContentType( MediaType.TEXT_PLAIN );
}
headersSet = true;
}
}