cap program drop ncreadtocsvbysec program define ncreadtocsvbysec syntax anything using/, csv(string) [Size(numlist integer) Origin(numlist integer >0)] local varname `anything' confirm name `varname' local csvfile `csv' removequotes,file(`using') local ncfile `r(file)' local ncfile = usubinstr(`"`ncfile'"',"\","/",.) local csvfile = usubinstr(`"`csvfile'"',"\","/",.) local no: word count `origin' if "`size'"==""{ forv j=1/`no'{ local size `size' -1 } } local nc: word count `size' if `nc' != `no' { di as error "The number of origin and size should be the same." exit } // Convert Stata 1-based indices to Java 0-based indices local origin0 forv i=1/`no'{ local oi: word `i' of `origin' local origin0 `origin0' `=`oi'-1' } // No need for any other validation - Java will handle it all di java: NCtoCSVbySection.main("`ncfile'","`csvfile'","`varname'","`origin0'","`size'") end cap program drop parsecsvopt program define parsecsvopt,rclass syntax anything, [replace] local file `anything' local replace `replace' removequotes,file(`"`file'"') local file `r(file)' local flag = fileexists(`"`file'"') if "`replace'"=="" & `flag'{ di as error "file exist, adding replace in csv() to overwrite it." exit 198 } return local file `file' cap program drop removequotes program define removequotes,rclass version 16 syntax, file(string) return local file `file' end java: /cp netcdfAll-5.6.0.jar import ucar.nc2.dataset.NetcdfDataset; import ucar.nc2.Variable; import ucar.ma2.Array; import ucar.nc2.Dimension; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import ucar.ma2.InvalidRangeException; import com.stata.sfi.*; public class NCtoCSVbySection { private static final int BUFFER_SIZE = 8192 * 1024; private static final long MAX_SAFE_ELEMENTS = 1_000_000_000; public static void main(String ncFilePath, String csvFilePath, String variableName, String start, String count) { int[] origin = parseIndices(start); int[] size = parseIndices(count); try (NetcdfDataset ncFile = NetcdfDataset.openDataset(ncFilePath)) { Variable mainVar = ncFile.findVariable(variableName); if (mainVar == null) { SFIToolkit.errorln("Variable " + variableName + " not found"); return; } List<Dimension> dims = mainVar.getDimensions(); // Validate dimensions match if (origin.length != dims.size() || size.length != dims.size()) { SFIToolkit.errorln("The number of origin and count should be equal # of axes in nc file."); return; } // Validate range bounds for each dimension for (int i = 0; i < dims.size(); i++) { int dimLength = dims.get(i).getLength(); // Check if origin is within bounds if (origin[i] < 0 || origin[i] >= dimLength) { SFIToolkit.errorln("Origin index " + (origin[i] + 1) + " is out of bounds for dimension " + dims.get(i).getShortName() + " with length " + dimLength); return; } // Handle special case: size[i] = -1 means "read to the end" int effectiveSize = size[i]; if (effectiveSize == -1) { effectiveSize = dimLength - origin[i]; size[i] = effectiveSize; // Update size for actual reading } // Check if origin + size is within bounds if (origin[i] + effectiveSize > dimLength) { SFIToolkit.errorln("Reading section exceeds bounds for dimension " + dims.get(i).getShortName() + ": origin=" + (origin[i] + 1) + ", size=" + effectiveSize + ", dimension length=" + dimLength); return; } } List<Variable> coordVars = new ArrayList<>(); Map<Variable, Integer> dimIndexMap = new HashMap<>(); for (int i = 0; i < dims.size(); i++) { Dimension dim = dims.get(i); Variable coordVar = ncFile.findVariable(dim.getShortName()); if (coordVar != null) { coordVars.add(coordVar); dimIndexMap.put(coordVar, i); } } long totalSize = calculateTotalSize(size); if (totalSize > MAX_SAFE_ELEMENTS) { SFIToolkit.errorln("Dataset too large"); return; } try (BufferedWriter writer = new BufferedWriter(new FileWriter(csvFilePath), BUFFER_SIZE)) { writeHeader(writer, coordVars, variableName); processData(writer, coordVars, mainVar, origin, size, totalSize, dimIndexMap); System.out.println("Data written to CSV: " + csvFilePath); } } catch (Exception e) { SFIToolkit.errorln(SFIToolkit.stackTraceToString(e)); } } private static int[] parseIndices(String str) { String[] parts = str.split(" "); int[] indices = new int[parts.length]; for (int i = 0; i < parts.length; i++) { indices[i] = Integer.parseInt(parts[i]); } return indices; } private static long calculateTotalSize(int[] size) { long total = 1; for (int s : size) total *= s; return total; } private static void writeHeader(BufferedWriter writer, List<Variable> coordVars, String varName) throws IOException { for (Variable var : coordVars) { writer.write(var.getShortName() + ","); } writer.write(varName + "\n"); } private static void processData(BufferedWriter writer, List<Variable> coordVars, Variable mainVar, int[] origin, int[] size, long totalSize, Map<Variable, Integer> dimIndexMap) throws IOException, InvalidRangeException { // é¢„åŠ è½½åæ ‡æ•°æ® List<double[]> coordCache = new ArrayList<>(); for (Variable var : coordVars) { Array fullData = var.read(); coordCache.add((double[]) fullData.get1DJavaArray(double.class)); } // 主å˜é‡åˆ‡ç‰‡è¯»å– Array mainData = mainVar.read(origin, size); double[] mainValues = (double[]) mainData.get1DJavaArray(double.class); // 生æˆç»´åº¦ç´¢å¼•数组 int[] dimIndexes = new int[coordVars.size()]; for (int i = 0; i < coordVars.size(); i++) { dimIndexes[i] = dimIndexMap.get(coordVars.get(i)); } // 预计算索引 int[] shape = size.clone(); int[][] indexMap = new int[(int) totalSize][]; for (long i = 0; i < totalSize; i++) { indexMap[(int)i] = calculateIndices(i, shape); } // 缓冲优化 StringBuilder sb = new StringBuilder(BUFFER_SIZE * 2); final int ROW_SIZE = coordVars.size() + 1; String[] rowBuffer = new String[ROW_SIZE]; for (long i = 0; i < totalSize; i++) { int[] indices = indexMap[(int)i]; // 处ç†åæ ‡å€¼ for (int j = 0; j < coordVars.size(); j++) { int actualDim = dimIndexes[j]; int pos = origin[actualDim] + indices[actualDim]; rowBuffer[j] = String.valueOf(coordCache.get(j)[pos]); } // 处ç†ä¸»å˜é‡å€¼ rowBuffer[coordVars.size()] = String.valueOf(mainValues[(int)i]); // 构建CSV行 sb.append(String.join(",", rowBuffer)).append("\n"); // 缓冲控制 if (sb.length() > BUFFER_SIZE) { writer.write(sb.toString()); sb.setLength(0); } } if (sb.length() > 0) { writer.write(sb.toString()); } } private static int[] calculateIndices(long index, int[] shape) { int[] indices = new int[shape.length]; for (int i = shape.length-1; i >= 0; i--) { indices[i] = (int)(index % shape[i]); index /= shape[i]; } return indices; } } end