Files
secondo/Javagui/viewer/hoese/algebras/raster2/DisplayRaster2ms.java
2026-01-23 17:03:45 +08:00

400 lines
12 KiB
Java

package viewer.hoese.algebras.raster2;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.awt.Font;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.MatteBorder;
import java.text.AttributedString;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import sj.lang.ListExpr;
import viewer.*;
import viewer.hoese.*;
import viewer.hoese.algebras.*;
import viewer.update.StandardFormatter;
import tools.Reporter;
/**
* Basic display class for <CODE>mstype</CODE> grid data (i.e. msint, msbool, msreal, msstring).
*/
public abstract class DisplayRaster2ms extends DisplayRaster2 implements Timed
{
private Grid3 grid;
/**
* Duration (the tile's "thickness" as number of grid duration units).
*/
private int tileSizeTime;
/**
* Additional temporal statistics
*/
int minCellIndexTime = Integer.MAX_VALUE;
int maxCellIndexTime = Integer.MIN_VALUE;
/**
* BoundingInterval of all cells with defined values
*/
protected Interval boundingInterval = null;
/**
* Intervals in which the msType has defined values.
*/
protected Vector intervals = new Vector(10, 5);
/**
* Formatter for list expressions (only for debugging).
*/
private StandardFormatter f = new StandardFormatter();
/********************************************************************************************************
* Methods of superclasses
*********************************************************************************************************/
/**
* {@inheritDoc}
*/
@Override
protected Grid3 getGrid()
{
return this.grid;
}
/**
* Computes the bounds of all objects displayed.
*/
@Override
protected void computeBounds()
{
super.computeBounds();
if (!this.intervals.isEmpty())
{
this.boundingInterval = ((Interval)this.intervals.get(0)).copy();
for (Object intvl : this.intervals)
{
this.boundingInterval.unionInternal((Interval)intvl);
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected int computeTimeIndex(double pTime)
{
Double timeCoord = Math.floor(pTime / this.getGrid().getDuration());
// Double timeCoord = Math.floor((pTime - this.getBoundingInterval().getStart()) / this.getGrid().getDuration());
return timeCoord.intValue();
}
/**
* {@inheritDoc}
* Scans the list representation of a msType datatype.
* The complete format of the msType list expression is: </br>
* ( (GridOriginX GridOriginY GridCellLength Duration) (TileSizeX TileSizeY TileSizeTime) ( KeyX KeyY KeyTime (Value*) )* ) </br>
* ( (double double double double) (int int int) (int int int ( Comparable* ) )* )
* @param le list representation of a msType datatype
* @see sj.lang.ListExpr
* @author Jasmine Ahmed
*/
@Override
protected void ScanValue(ListExpr le)
{
// Check if the list expression contains anything at all,
// if grid and tile dimensions are defined and
// if list expression contains a list of tiles (possibly empty)
if(le == null
|| le.first() == null
|| le.second() == null
|| le.third() == null)
{
Reporter.writeError("No valid list expression for msType: " );
err = true;
return;
}
// scan grid definition
this.scanValueGridDef(le.first());
// scan tile definition
this.scanValueTileDef(le.second());
// scan list of tiles
ListExpr leTiles = le.rest().rest();
while (!leTiles.isEmpty())
{
ListExpr leNextTile = leTiles.first();
// scan single tile
this.scanValueTile(leNextTile);
leTiles = leTiles.rest();
}
// set flag so list expression will be scanned only once
this.isScanned = true;
}
/**
* Get info text.
*/
@Override
public String getInfo()
{
String eol = System.getProperty("line.separator", "\n");
StringBuffer sb = new StringBuffer(super.getInfo());
if (this.isScanned)
{
sb.append("Bounding time interval: ").append(this.boundingInterval.toString()).append(eol);
}
return sb.toString();
}
@Override
public String toString()
{
return this.getAttrName();
}
/*********************************************************************************
* Methods of implemented interfaces
*********************************************************************************/
/**
* {@inheritDoc}
* Method of interface Timed.
*/
public Interval getBoundingInterval()
{
return this.boundingInterval;
}
/**
* {@inheritDoc}
* Method of interface Timed.
*/
public Vector getIntervals()
{
return this.intervals;
}
/**
* {@inheritDoc}
* Method of interface Timed.
* The "time renderer" for msTypes is a panel with one label for each interval with defined values.
*/
public JPanel getTimeRenderer(double PixelTime)
{
JPanel jp = new JPanel(null);
if (intervals == null)
return null;
ListIterator li = intervals.listIterator();
while (li.hasNext()) {
Interval in = (Interval)li.next();
int start = (int)((in.getStart() - this.boundingInterval.getStart())*PixelTime);
int end = (int)((in.getEnd() - this.boundingInterval.getStart())*PixelTime);
JLabel jc = new JLabel();
// jc.setFont(new Font("Dialog", Font.PLAIN, 12));
jc.setPreferredSize(new Dimension(1, 15));
jc.setBorder(new MatteBorder(2, (in.isLeftclosed()) ? 2 : 0, 2, (in.isRightclosed()) ? 2 : 0, Color.BLACK));
Dimension d = jc.getPreferredSize();
jc.setBounds(start, 10, end-start, 20);
// jc.setBounds(start, (int)d.getHeight()*0 + 7, end - start, (int)d.getHeight());
jc.setToolTipText(LEUtils.convertTimeToString(in.getStart())
+ "..." + LEUtils.convertTimeToString(in.getEnd()));
jp.setPreferredSize(new Dimension((int)((this.boundingInterval.getEnd()
- this.boundingInterval.getStart())*PixelTime), 25));
jp.add(jc);
}
return jp;
}
/*************************************************************************************
* Methods of class itself
*************************************************************************************/
/**
* Scans the list representation of a msType grid definition, i.e. a list expression of the format:
* (double double double double) signifying (GridOriginX GridOriginY GridCellLength GridOriginTime)
*/
protected void scanValueGridDef(ListExpr leGridDef)
{
// Check number and type of list elements,
if (leGridDef.listLength() != 4
|| leGridDef.first().atomType() != ListExpr.REAL_ATOM
|| leGridDef.second().atomType() != ListExpr.REAL_ATOM
|| leGridDef.third().atomType() != ListExpr.REAL_ATOM
|| leGridDef.fourth().atomType() != ListExpr.REAL_ATOM)
{
Reporter.writeError("No correct list expression for msType grid: " );
err = true;
return;
}
double gridOriginX = leGridDef.first().realValue();
double gridOriginY = leGridDef.second().realValue();
double gridCellLength = leGridDef.third().realValue();
double gridDuration = leGridDef.fourth().realValue();
this.grid = new Grid3(gridOriginX, gridOriginY, gridCellLength, gridDuration);
}
/**
* Scans the list representation of a msType tile definition, i.e. a list expression of the format:
* (int int int) signifying (TileSizeX TileSizeY TileSizeTime)
*/
protected void scanValueTileDef(ListExpr leTileDef)
{
// Check number and type of list elements,
if (leTileDef.listLength() != 3
|| leTileDef.first().atomType() != ListExpr.INT_ATOM
|| leTileDef.second().atomType() != ListExpr.INT_ATOM
|| leTileDef.third().atomType() != ListExpr.INT_ATOM)
{
Reporter.writeError("No correct list expression for msType tile definition: " );
err = true;
return;
}
int tileSizeX = leTileDef.first().intValue();
int tileSizeY = leTileDef.second().intValue();
int tileSizeTime = leTileDef.third().intValue();
this.grid.setTileDefinition(tileSizeX, tileSizeY, tileSizeTime);
}
/**
* Scans the list representation of a msType tile, i.e. a list expression of the format:
* (int int int (Comparable*)) signifying (TileIndexX TileIndexY TileIndexTime (Value*))
*/
protected void scanValueTile(ListExpr leTile)
{
if (leTile.listLength() != 4
|| leTile.first().atomType() != ListExpr.INT_ATOM
|| leTile.second().atomType() != ListExpr.INT_ATOM
|| leTile.third().atomType() != ListExpr.INT_ATOM)
{
Reporter.writeError("No correct list expression for msType tile: " );
err = true;
return;
}
// read indices of cell in the tile's lower left corner
// index signifying north/south index from grid origin
Integer tileIndexX = leTile.first().intValue();
// index signifying east/west index from grid origin
Integer tileIndexY = leTile.second().intValue();
// index signifying start
Integer tileIndexTime = leTile.third().intValue();
//*************************************************************************************
// scan all cell values in a partial raster
//*************************************************************************************
ListExpr leValues = leTile.fourth();
int cellsPerTile = this.getGrid().getTileSizeX() * this.getGrid().getTileSizeY();
int tileSizeTime = this.getGrid().getTileSizeTime();
double duration = this.getGrid().getDuration();
if (leValues.listLength() != cellsPerTile*tileSizeTime)
{
Reporter.writeError("No correct list expression for tile values (expected " + (cellsPerTile*tileSizeTime) + " values per tile, got " + leValues.listLength() + "). ");
err = true;
return;
}
for (int t=0; t<tileSizeTime; t++)
{
Comparable[] tileValues = new Comparable[cellsPerTile];
boolean sliceHasDefinedValues = false;
int[] offsetX = this.getGrid().getOffsetX();
int[] offsetY = this.getGrid().getOffsetY();
// int[] offsetTime = this.getGrid().getOffsetTime();
int timeSliceIndex = tileIndexTime + t;
this.minCellIndexTime = Math.min(minCellIndexTime,timeSliceIndex);
this.maxCellIndexTime = Math.max(maxCellIndexTime,timeSliceIndex);
for (int i=0; i<cellsPerTile; i++)
{
Comparable val = this.scanValueAtom(leValues.first());
if (val != null)
{
tileValues[i] = val;
sliceHasDefinedValues = true;
// update statistics
int cellCoordX = tileIndexX + offsetX[i];
int cellCoordY = tileIndexY + offsetY[i];
minCellCoordX = Math.min(minCellCoordX,cellCoordX);
maxCellCoordX = Math.max(maxCellCoordX,cellCoordX);
minCellCoordY = Math.min(minCellCoordY,cellCoordY);
maxCellCoordY = Math.max(maxCellCoordY,cellCoordY);
this.updateMinMaxValue(val);
cellCount++;
}
// get next value
leValues = leValues.rest();
}
if (sliceHasDefinedValues)
{
// store value array and indices of lower left cell in map
this.setRasterValuesPartial(tileValues, new Point(tileIndexX, tileIndexY), timeSliceIndex);
// store interval of the time slice
double start = timeSliceIndex*duration;
double end = (timeSliceIndex+1)*duration;
Interval interval = new Interval(start, end, true, false);
this.addInterval(interval);
}
}
}
/**
* Adds an interval to the list of defined intervals.
* If the new interval overlaps with an existing interval, the two are merged.
*/
protected void addInterval(Interval pInterval)
{
for (Object interval : this.intervals)
{
if (pInterval.connected((Interval)interval))
{
((Interval)interval).unionInternal(pInterval);
return;
}
}
this.intervals.add(pInterval);
}
}