/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program 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 General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.beam.smos.visat.export; import com.bc.ceres.binio.*; import org.esa.beam.dataio.smos.SmosConstants; import org.esa.beam.smos.DateTimeUtils; import org.esa.beam.smos.SmosUtils; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.util.Date; import java.util.HashMap; class EEExportGridPointHandler implements GridPointHandler { private final DataContext targetContext; private final GridPointFilter targetFilter; private final HashMap<Long, Date> snapshotIdTimeMap; private final TimeTracker timeTracker; private final GeometryTracker geometryTracker; private boolean level2; private long gridPointCount; private long gridPointDataPosition; private int latIndex; private int lonIndex; private static final int SEGMENT_SIZE = 16384; EEExportGridPointHandler(DataContext targetContext) { this(targetContext, new GridPointFilter() { @Override public boolean accept(int id, CompoundData gridPointData) throws IOException { return true; } }); } EEExportGridPointHandler(DataContext targetContext, GridPointFilter targetFilter) { this.targetContext = targetContext; this.targetFilter = targetFilter; snapshotIdTimeMap = new HashMap<>(); timeTracker = new TimeTracker(); geometryTracker = new GeometryTracker(); final String formatName = targetContext.getFormat().getName(); level2 = SmosUtils.isL2Format(formatName); } @Override public void handleGridPoint(int id, CompoundData gridPointData) throws IOException { if (gridPointCount == 0) { init(gridPointData); } if (targetFilter.accept(id, gridPointData)) { trackSensingTime(gridPointData); trackGeometry(gridPointData); targetContext.getData().setLong(SmosConstants.GRID_POINT_COUNTER_NAME, ++gridPointCount); // ATTENTION: flush must occur <em>before</em> grid point data is written (rq-20091008) targetContext.getData().flush(); gridPointData.resolveSize(); final long size = gridPointData.getSize(); final byte[] bytes = new byte[(int) size]; get(gridPointData, bytes); put(targetContext, bytes, gridPointDataPosition); gridPointDataPosition += size; } } boolean hasValidPeriod() { return timeTracker.hasValidPeriod(); } Date getSensingStart() { return timeTracker.getIntervalStart(); } Date getSensingStop() { return timeTracker.getIntervalStop(); } boolean hasValidArea() { return geometryTracker.hasValidArea(); } Rectangle2D getArea() { return geometryTracker.getArea(); } long getGridPointCount() { return gridPointCount; } static Date getL2MjdTimeStamp(CompoundData compoundData) throws IOException { int index = compoundData.getType().getMemberIndex("Mean_Acq_Time"); if (index > 0) { return getOSUDPDate(compoundData, index); } final CompoundType type = compoundData.getType(); index = type.getMemberIndex("Days"); if (index >= 0) { return getSMUPDDate(compoundData); } return null; } private static Date getSMUPDDate(CompoundData compoundData) throws IOException { final int days = compoundData.getInt("Days"); final long seconds = compoundData.getUInt("Seconds"); final long microseconds = compoundData.getUInt("Microseconds"); if ((days + seconds + microseconds) == 0) { return null; } return DateTimeUtils.cfiDateToUtc(days, seconds, microseconds); } private static Date getOSUDPDate(CompoundData compoundData, int index) throws IOException { final float floatDate = compoundData.getFloat(index); if (floatDate > 0.f) { return DateTimeUtils.mjdFloatDateToUtc(floatDate); } return null; } private void trackSensingTime(CompoundData gridPointData) throws IOException { final CompoundType type = gridPointData.getType(); final String typeName = type.getName(); if (typeName.contains("ECMWF")) { return; // no sensing time information in ECMWF auxiliary files } if (level2) { final Date timeStamp = getL2MjdTimeStamp(gridPointData); if (timeStamp != null) { timeTracker.track(timeStamp); } } else { int index = type.getMemberIndex(SmosConstants.BT_DATA_LIST_NAME); final SequenceData btDataList = gridPointData.getSequence(index); CompoundData btData = btDataList.getCompound(0); trackTime(btData); final int elementCount = btDataList.getElementCount(); btData = btDataList.getCompound(elementCount - 1); trackTime(btData); } } private void trackTime(CompoundData btData) throws IOException { int index; index = btData.getType().getMemberIndex(SmosConstants.BT_SNAPSHOT_ID_OF_PIXEL_NAME); if (index >= 0) { final long snapShotId = btData.getUInt(index); timeTracker.track(snapshotIdTimeMap.get(snapShotId)); } } private void trackGeometry(CompoundData gridPointData) throws IOException { double lat = gridPointData.getDouble(latIndex); double lon = gridPointData.getDouble(lonIndex); // normalisation to [-180, 180] necessary for some L1c test products if (lon > 180.0) { lon = lon - 360.0; } geometryTracker.add(new Point2D.Double(lon, lat)); } private void init(CompoundData gridPointData) throws IOException { final CompoundType gridPointType = gridPointData.getType(); latIndex = gridPointType.getMemberIndex(SmosConstants.GRID_POINT_LAT_NAME); lonIndex = gridPointType.getMemberIndex(SmosConstants.GRID_POINT_LON_NAME); final CollectionData parent = gridPointData.getParent(); final long parentPosition = parent.getPosition(); copySnapshotData(parent, parentPosition); createSnapshotIdMap(parent); targetContext.getData().setLong(SmosConstants.GRID_POINT_COUNTER_NAME, 0); targetContext.getData().flush(); gridPointDataPosition = parentPosition; } private void createSnapshotIdMap(CollectionData parent) throws IOException { final DataContext context = parent.getContext(); final int snapshotListIndex = context.getData().getMemberIndex(SmosConstants.SNAPSHOT_LIST_NAME); if (snapshotListIndex == -1) { return; // we have a browse product } final SequenceData snapshotData = context.getData().getSequence(snapshotListIndex); final int snapshotCount = snapshotData.getElementCount(); for (int i = 0; i < snapshotCount; i++) { final CompoundData snapshot = snapshotData.getCompound(i); final Date snapshotTime = DateTimeUtils.cfiDateToUtc(snapshot); final long snapshotId = snapshot.getUInt(1); snapshotIdTimeMap.put(snapshotId, snapshotTime); } } private void copySnapshotData(CollectionData parent, long parentPosition) throws IOException { copyBytesTo(parent.getContext(), targetContext, parentPosition); } private static void copyBytesTo(DataContext sourceContext, DataContext targetContext, long to) throws IOException { byte[] bytes = new byte[SEGMENT_SIZE]; for (long pos = 0; pos < to; pos += SEGMENT_SIZE) { final long remainderSize = to - pos; if (remainderSize < SEGMENT_SIZE) { bytes = new byte[(int) remainderSize]; } get(sourceContext, bytes, pos); put(targetContext, bytes, pos); } } private static void get(CompoundData compoundData, byte[] bytes) throws IOException { final DataContext context = compoundData.getContext(); final long position = compoundData.getPosition(); context.getHandler().read(context, bytes, position); } private static void get(DataContext sourceContext, byte[] bytes, long position) throws IOException { sourceContext.getHandler().read(sourceContext, bytes, position); } private static void put(DataContext targetContext, byte[] bytes, long position) throws IOException { targetContext.getHandler().write(targetContext, bytes, position); } }