/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2007-2014, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.imageio.netcdf;
import it.geosolutions.imageio.stream.eraf.EnhancedRandomAccessFile;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.util.List;
/**
* A bean that represents a row in the index used for mapping 2d grids to 2d slices in NetCDF files.
*
* <p>
* The elements are:
* <ol>
* <li><b>imageIndex</b> the index of the image to work with</li>
* <li><b>tIndex</b> the index of the time dimension for this 2d slice</li>
* <li><b>zIndex</b> the index of the elevation dimension for this 2d slice</li>
* <li><b>variableName</b> the name of this variable, e.g. temperature</li>
* </ol>
*
* @author Andrea Antonello
* @author Simone Giannecchini, GeoSolutions
*
*/
public class Slice2DIndex {
/** DEFAULT_INDEX */
public static final int DEFAULT_INDEX = -1;
private int[] index;
private final String variableName;
public Slice2DIndex(String variableName) {
this(new int[] {}, variableName);
}
public Slice2DIndex(int[] index, String variableName) {
org.geotools.util.Utilities.ensureNonNull("variableName", variableName);
org.geotools.util.Utilities.ensureNonNull("index", index);
this.index = index;
this.variableName = variableName;
}
public int getNIndex(int n) {
return index.length > 0 ? index[n] : DEFAULT_INDEX;
}
public int getNCount() {
return index.length;
}
public String getVariableName() {
return variableName;
}
@Override
public String toString() {
return "UnidataVariableIndex [index=" + index + ", variableName=" + variableName + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
for (int i = 0; i < index.length; i++) {
result = prime * result + index[i]; }
result = prime * result + ((variableName == null) ? 0 : variableName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Slice2DIndex other = (Slice2DIndex) obj;
if (variableName == null) {
if (other.variableName != null)
return false;
} else if (!variableName.equals(other.variableName))
return false;
if (index == null) {
if (other.index != null) {
return false;
}
else {
return true;
}
} else if (other.index == null) {
if (index != null) {
return false;
} else {
return true;
}
} else if (index.length != other.index.length) {
return false;
} else {
for (int i = 0; i < index.length; i++) {
if (index[i] != other.index[i]) {
return false;
}
}
}
return true;
}
/**
* An wrapper for variable index files.
*
* @author Andrea Antonello
* @author Simone Giannecchini, GeoSolutions
*
*/
public static class Slice2DIndexManager {
private static final long ADDRESS_SIZE = 8l;
private static long ADDRESS_POSITION = 4l;
private EnhancedRandomAccessFile raf;
private File file;
private int numberOfRecords;
public Slice2DIndexManager(File file) {
this.file = file;
}
public void open() throws IOException {
raf = new EnhancedRandomAccessFile(file, "r");
raf.setByteOrder(ByteOrder.BIG_ENDIAN);
numberOfRecords = raf.readInt();
}
/**
* Read a {@link Slice2DIndex} from file given the imageIndex.
*
* @param imageIndex the imageIndex to look for.
* @return the {@link Slice2DIndex} for the picked image.
* @throws IOException
*/
public synchronized Slice2DIndex getSlice2DIndex(int imageIndex) throws IOException {
// Synchronized these access due to the RAF usage.
// concurrent seeks and reads on the same RAF may
// may result into unexpected results
long addressPosition = ADDRESS_POSITION + imageIndex * ADDRESS_SIZE;
raf.seek(addressPosition);
long dataPosition = raf.readLong();
long endDataPosition = raf.readLong();
raf.seek(dataPosition);
int nextValue = raf.readInt();
int[] index;
if (nextValue < 0) {
int dimensions = -nextValue;
index = new int[dimensions];
for (int i = 0; i < dimensions; i++) {
index[i] = raf.readInt();
}
} else { //backwards compatibility
index = new int[2];
index[VariableAdapter.T] = nextValue;
index[VariableAdapter.Z] = raf.readInt();
}
int stringSize = (int) (endDataPosition - raf.getFilePointer());
byte[] stringBytes = new byte[stringSize];
raf.read(stringBytes);
String varName = new String(stringBytes);
return new Slice2DIndex(index, varName);
}
public void dispose() throws IOException {
if (raf != null) {
raf.close();
}
}
/**
* Utility method to write an index file.
*
* @param file the file to write to.
* @param indexList the list of {@link Slice2DIndex} to dump to file.
* @throws IOException
*/
public static void writeIndexFile(File file, List<Slice2DIndex> indexList) throws IOException {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(file, "rw");
int size = indexList.size();
// write number of records
raf.writeInt(size);
long dataPosition = ADDRESS_POSITION + (size + 1) * ADDRESS_SIZE; // the +1 is to have the end address
long[] pointer = new long[size];
raf.seek(dataPosition);
for( int i = 0; i < size; i++ ) {
Slice2DIndex sliceNDIndex = indexList.get(i);
long pos = raf.getFilePointer();
pointer[i] = pos;
//write as negative value, so if negative is missing -> old file (backwards compatibility)
raf.writeInt(-sliceNDIndex.getNCount());
for (int j = 0; j < sliceNDIndex.getNCount(); j++) {
raf.writeInt(sliceNDIndex.getNIndex(j));
}
raf.write(sliceNDIndex.getVariableName().getBytes());
}
long dataEnd = raf.getFilePointer();
raf.seek(ADDRESS_POSITION);
for( long address : pointer ) {
raf.writeLong(address);
}
// add also the data end position
raf.writeLong(dataEnd);
} finally {
if (raf != null) {
raf.close();
}
}
}
public int getNumberOfRecords() throws IOException {
return numberOfRecords;
}
}
}