ecological-engine-geospatia.../src/main/java/density/WorldImageProducer.java

1021 lines
29 KiB
Java

package density;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import javax.imageio.ImageIO;
// Referenced classes of package density:
// GridDimension, LazyGrid, Grid, Utils,
// Sample
public class WorldImageProducer extends Canvas
{
public boolean gridnull = false;
public BufferedImage img;
public int scale;
public int mode;
public static final int LOG = 0;
public static final int PLAIN = 1;
public static final int CLASS = 2;
public int classColor[];
public String className[];
public Grid grid;
public boolean blackandwhite;
public boolean redandyellow;
public boolean dichromatic;
public Color dichromaticColors[];
public static boolean toggleSampleColor = true;
public int background;
public double breakpoint;
public int pixels[];
public int minx;
public int maxx;
public int miny;
public int maxy;
public Sample samples[];
public Sample testsamples[];
public boolean visible;
public boolean makeLegend;
public boolean makeTimeline=false;
public String initialTimeLabel;
public String timeLabel;
public int maxTimeIntervals;
public int timeIndex;
public static boolean setNumCategoriesByMax = false;
public static boolean makeNorth = false;
public static int defaultSampleRadius = 7;
public static int adjustSampleRadius = 0;
public static int numCategories = 14;
public static double categories[] = null;
public static int maxFracDigits = -1;
public static double divisor = 2D;
public double minval;
public double maxval;
public int xOffset;
public int yOffset;
public static double xline = -1D;
public static double yline = -1D;
public int linerow;
public int linecol;
public int white;
public int black;
public int sampleColor;
public int testSampleColor;
public int sampleRadius;
public double min;
public double max;
public int maxborder;
public int bdist[][];
public static boolean addTinyVals = true;
double aspect(int x1, int x2, int y1, int y2)
{
return (double)(y2 - y1) / (double)(x2 - x1);
}
void setZoom(int x1, int x2, int y1, int y2)
{
minx = x1 >= x2 ? x2 : x1;
maxx = x1 <= x2 ? x2 : x1;
miny = y1 >= y2 ? y2 : y1;
maxy = y1 <= y2 ? y2 : y1;
if(nozoom())
{
return;
}
double target = aspect(0, getCols(), 0, getRows());
if(aspect(minx, maxx, miny, maxy) < target)
{
maxy = miny + (int)(target * (double)(maxx - minx));
} else
{
maxx = minx + (int)((double)(maxy - miny) / target);
}
}
void zoomOut()
{
if(nozoom())
{
return;
}
if(minx == 0 && maxx == getCols() && miny == 0 && maxy == getRows())
{
return;
} else
{
minx = 0;
maxx = getCols();
miny = 0;
maxy = getRows();
makeImage();
return;
}
}
public void setClassNames(String s)
{
setClassNames(s.split(":"));
}
public void setClassNames(String s[])
{
className = s;
if(minval == -1D)
{
minval = 0.0D;
maxval = className.length - 1;
numCategories = className.length;
} else
{
numCategories = (int)((maxval - minval) + 1.0D);
}
mode = 1;
}
int[] stringToColors(String s)
{
String colors[] = s.split(" ");
int result[] = new int[colors.length];
for(int i = 0; i < colors.length; i++)
{
if(colors[i].indexOf("|") != -1)
{
String rgb[] = colors[i].split("\\|");
int rgbi[] = new int[3];
for(int j = 0; j < 3; j++)
{
rgbi[j] = Integer.parseInt(rgb[j]);
}
result[i] = (new Color(rgbi[0], rgbi[1], rgbi[2])).getRGB();
continue;
}
try
{
result[i] = Integer.decode(colors[i]).intValue();
continue;
}
catch(NumberFormatException e) { }
try
{
result[i] = ((Color)Class.forName("java.awt.Color").getField(colors[i]).get(null)).getRGB();
}
catch(Exception ee)
{
throw new NumberFormatException((new StringBuilder()).append("Invalid color: ").append(colors[i]).toString());
}
}
return result;
}
public void setColorClasses(String s)
{
classColor = stringToColors(s);
mode = 2;
}
static void setCategories(String s)
{
String cats[] = s.split(" ");
categories = new double[cats.length];
for(int i = 0; i < cats.length; i++)
{
categories[i] = Double.parseDouble(cats[i]);
}
numCategories = cats.length;
}
void setline()
{
linerow = linecol = -1;
if(xline != -1D)
{
linerow = grid.getDimension().toRow(xline) * scale;
}
if(yline != -1D)
{
linecol = grid.getDimension().toCol(yline) * scale;
}
}
public void setMinval(double m)
{
minval = m;
}
public void setMaxval(double m)
{
maxval = m;
}
public void setMode(int i)
{
mode = i;
}
void setBreakpoint(double x)
{
breakpoint = x;
}
void setColorScheme(int i)
{
blackandwhite = i == 0;
}
public void setGrid(Grid grid, int minrows, int mincols)
{
this.grid = grid;
scale = 1;
double xscale = Math.floor((double)mincols / (double)getCols());
double yscale = Math.floor((double)minrows / (double)getRows());
scale = (int)(xscale >= yscale ? yscale : xscale);
if(scale < 1)
{
scale = 1;
}
img = new BufferedImage(getCols(), getRows(), 1);
pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
setZoom(-1, -1, -1, -1);
setline();
if(blackandwhite)
{
newborder();
}
}
void setSamples(Sample s[])
{
samples = s;
}
public void setTestSamples(Sample s[])
{
testsamples = s;
}
double max(double x, double y)
{
return x <= y ? y : x;
}
public void setTime(int maxTimeFrames, int timeFrame,String initialTimeLabel,String currentTimeLabel){
maxTimeIntervals=maxTimeFrames;
timeIndex=timeFrame;
this.initialTimeLabel=initialTimeLabel;
this.timeLabel=currentTimeLabel;
}
GridDimension viewDimension()
{
GridDimension dim = grid.getDimension();
double cs = dim.getcellsize() / (double)scale;
return new GridDimension(dim.getxllcorner() + max(minx, 0.0D) * cs, dim.getyllcorner() + (double)(maxy != -1 ? getRows() - maxy : 0) * cs, cs * (maxx != -1 ? (double)(maxx - minx) / (double)getCols() : 1.0D), getRows(), getCols());
}
int getRows()
{
if (grid!=null)
return scale * grid.getDimension().nrows;
else
return scale * 480;
}
int getCols()
{
if (grid!=null)
return scale * grid.getDimension().ncols;
else
return scale * 640;
}
boolean inBounds(int r, int c)
{
return r >= 0 && r < getRows() && c >= 0 && c < getCols();
}
public WorldImageProducer(Grid grid, int minrows, int mincols)
{
blackandwhite = false;
redandyellow = false;
dichromatic = false;
dichromaticColors = (new Color[] {
Color.red, Color.blue
});
breakpoint = -9999D;
minx = -1;
maxx = -1;
miny = -1;
maxy = -1;
samples = null;
testsamples = null;
visible = true;
makeLegend = true;
minval = -1D;
maxval = -1D;
xOffset = -1;
yOffset = -1;
white = -1;
black = Color.black.getRGB();
sampleColor = white;
testSampleColor = 0xff8a2be2;
sampleRadius = defaultSampleRadius;
min = 0.0D;
max = 0.0D;
maxborder = 5;
setGrid(grid, minrows, mincols);
}
public WorldImageProducer(Grid grid)
{
blackandwhite = false;
redandyellow = false;
dichromatic = false;
dichromaticColors = (new Color[] {
Color.red, Color.blue
});
breakpoint = -9999D;
minx = -1;
maxx = -1;
miny = -1;
maxy = -1;
samples = null;
testsamples = null;
visible = true;
makeLegend = true;
minval = -1D;
maxval = -1D;
xOffset = -1;
yOffset = -1;
white = -1;
black = Color.black.getRGB();
sampleColor = white;
testSampleColor = 0xff8a2be2;
sampleRadius = defaultSampleRadius;
min = 0.0D;
max = 0.0D;
maxborder = 5;
if (grid!=null){
setGrid(grid, 1200, 1600);
}
else
gridnull=true;
}
boolean nozoom()
{
return minx == -1 || maxx == -1 || miny == -1 || maxy == -1;
}
int windowx2imgx(int x)
{
return (int)(((double)getCols() / (double)getSize().width) * (double)x);
}
int windowy2imgy(int y)
{
return (int)(((double)getRows() / (double)getSize().height) * (double)y);
}
int gridrow(int r)
{
if(nozoom())
{
return r / scale;
} else
{
int rr = (int)((double)miny + ((double)r / (double)getRows()) * (double)(maxy - miny));
return rr / scale;
}
}
int gridcol(int c)
{
if(nozoom())
{
return c / scale;
} else
{
int cc = (int)((double)minx + ((double)c / (double)getCols()) * (double)(maxx - minx));
return cc / scale;
}
}
boolean hasData(int r, int c)
{
if (!gridnull)
return grid.hasData(gridrow(r), gridcol(c));
else
return false;
}
float eval(int r, int c)
{
return grid.eval(gridrow(r), gridcol(c));
}
public void makeImage()
{
boolean start = true;
background = redandyellow ? 0x8080ff : blackandwhite ? white : 0xff000000;
if(minval != -1D || maxval != -1D)
{
min = minval;
max = maxval;
} else
{
for(int i = 0; i < getRows(); i++)
{
for(int j = 0; j < getCols(); j++)
{
if(!hasData(i, j))
{
continue;
}
if(start)
{
min = max = eval(i, j);
start = false;
continue;
}
float val = eval(i, j);
if((double)val < min && (mode != 0 || val > 0.0F) || min <= 0.0D && mode == 0)
{
min = val;
}
if((double)val > max)
{
max = val;
}
}
}
if(grid instanceof LazyGrid)
{
try
{
((LazyGrid)grid).initialize();
}
catch(IOException e)
{
Utils.fatalException((new StringBuilder()).append("Error initializing file ").append(grid.name).toString(), null);
}
}
}
if(max == 100D && min >= 0.0D && min < 1.0000000000000001E-005D)
{
min = 1.0000000000000001E-005D;
}
if(max == 100D && min >= 0.0D && min < 0.001D && blackandwhite)
{
min = 0.01D;
}
if(min > 0.0D && max / min > 1000000000000000D)
{
min = max / 1000000000000000D;
}
if(min >= 0.0D && min <= 0.10000000000000001D && max >= 0.69999999999999996D && max <= 1.0D && mode != 0 && maxval == -1D)
{
min = 0.0D;
max = 1.0D;
numCategories = 11;
}
for(int i = 0; i < getRows(); i++)
{
Utils.reportProgress((double)(i * 100) / (double)getRows());
for(int j = 0; j < getCols(); j++)
{
pixels[i * getCols() + j] = i != linerow ? j != linecol ? hasData(i, j) ? showColor(eval(i, j), min, max) : !blackandwhite || !isborder(i, j) ? background : black : white : white;
if (pixels[i * getCols() + j] == black && !blackandwhite) //patch by Gianpaolo Coro to exclude black colors
pixels[i * getCols() + j] = Color.LIGHT_GRAY.getRGB();
}
}
int sr = sampleRadius;
int rr = getRows() * scale;
int cc = getCols() * scale;
if(scale > 1)
{
sr = (int)Math.ceil((double)sr / (double)scale);
}
if(rr < 900 && cc < 900 && sr * scale >= 5 && sr > 2)
{
sr -= 2;
}
if(rr < 600 && cc < 600 && sr * scale >= 5 && sr > 2)
{
sr -= 2;
}
if(rr < 300 && cc < 300 && sr * scale >= 5 && sr > 2)
{
sr -= 2;
}
if(rr > 2000 || cc > 2000)
{
sr += 1 / scale;
}
if(rr > 4000 || cc > 4000)
{
sr += 1 / scale;
}
sr += adjustSampleRadius;
if(samples != null)
{
showSamples(samples, sampleColor, sr);
}
if(testsamples != null)
{
showSamples(testsamples, testSampleColor, sr);
}
if(makeLegend)
{
makeLegend();
}
if(makeNorth)
{
makeNorth();
}
if(visible)
{
repaint();
}
if(makeTimeline)
makeTimeline(maxTimeIntervals,timeIndex,initialTimeLabel,timeLabel);
}
void newborder()
{
int nr = getRows();
int nc = getCols();
bdist = new int[nr][nc];
for(int i = 0; i < getRows(); i++)
{
for(int j = 0; j < getCols(); j++)
{
bdist[i][j] = hasData(i, j) ? 0 : 0x186a0;
}
}
for(int iter = 0; iter < maxborder; iter++)
{
for(int i = 0; i < nr; i++)
{
for(int j = 0; j < nc; j++)
{
for(int id = -1; id <= 1; id += 2)
{
for(int jd = -1; jd <= 1; jd += 2)
{
int ii = i + id;
int jj = j + jd;
if(ii >= 0 && jj >= 0 && ii < nr && jj < nc && bdist[ii][jj] < bdist[i][j] - 1)
{
bdist[i][j] = bdist[ii][jj] + 1;
}
}
}
}
}
}
}
boolean isborder(int i, int j)
{
return bdist[i][j] < maxborder;
}
void showSamples(Sample samples[], int color, int sr)
{
GridDimension dim = viewDimension();
int under[] = null;
if(blackandwhite && toggleSampleColor)
{
under = (int[])(int[])pixels.clone();
}
for(int i = 0; i < samples.length; i++)
{
if (samples[i]==null)
continue;
int r = samples[i].getRow(dim);
int c = samples[i].getCol(dim);
int color2 = color;
if(blackandwhite && toggleSampleColor)
{
int cnt = 0;
int tot = 0;
for(int j = (-sr + 1) * scale; j < sr * scale; j++)
{
for(int k = (-sr + 1) * scale; k < sr * scale; k++)
{
if(inBounds(r + j, c + k))
{
cnt++;
tot += (new Color(under[(r + j) * getCols() + (c + k)])).getBlue();
}
}
}
if(cnt > 0 && tot / cnt > 128)
{
color2 = black;
}
} else
if(blackandwhite)
{
color2 = black;
}
for(int j = (-sr + 1) * scale; j < sr * scale; j++)
{
for(int k = (-sr + 1) * scale; k < sr * scale; k++)
{
if(inBounds(r + j, c + k))
{
pixels[(r + j) * getCols() + (c + k)] = color2;
}
}
}
}
}
void makeTimeline(int maxtimeframes, int timeidx, String initialTimeLabel, String timeLabel)
{
int num = timeidx+1;
int fontSize = getRows() <= 2000 && getCols() <= 2000 ? ((int) (getRows() <= 1000 && getCols() <= 1000 ? 11 : 18)) : 24;
Graphics2D image = (Graphics2D)img.getGraphics();
Font font = new Font("Dialog", 1, fontSize);
image.setFont(font);
FontMetrics fm = image.getFontMetrics(font);
// int height = fm.getHeight() + 2;
String labels[] = new String[timeidx+1];
labels[0] = initialTimeLabel;
labels[timeidx] = timeLabel;
int w = fm.stringWidth(labels[timeidx]);
int width = fm.getWidths()[0] + w;
int heigh = fm.getWidths()[0] ;
//computeOffsets(maxtimeframes + 6 + 2 * height, (timeidx+1 + 2) * height);
int y = getRows()-50;
int x0 = (getCols()/2)-(maxtimeframes/2);
for(int i = 0; i < num; i++)
{
//((num - i) + 1) * (height-yOffset);
image.setColor(Color.white);
int x = (i+1) + x0;
if (!initialTimeLabel.equals(timeLabel))
{
image.fill(new Rectangle(x, y, width, heigh));
image.setColor(Color.white);
}
String label = labels[i]==null?"":labels[i];
if (i==0)
image.drawString(label, (x-width/2), (y +heigh+ 15));
else
image.drawString(label, (x+width), (y +heigh+ 15));
}
}
void makeLegend()
{
int num = setNumCategoriesByMax ? (int)max + 1 : numCategories;
double vals[] = categories != null ? categories : new double[num];
int fontSize = getRows() <= 2000 && getCols() <= 2000 ? ((int) (getRows() <= 1000 && getCols() <= 1000 ? 11 : 18)) : 24;
if(categories == null)
{
if(mode == 0)
{
double x = max;
if(max > 50D && max <= 100D)
{
for(int i = 0; i < num; i++)
{
vals[i] = x;
x /= divisor;
}
if(addTinyVals)
{
vals[num - 3] = 0.01D;
vals[num - 2] = 0.001D;
vals[num - 1] = min <= 0.0001D ? min : 0.0001D;
} else
{
vals[num - 1] = 0.0D;
}
} else
{
double div = Math.exp(Math.log(min >= max / 1000000000000000D ? max / min : 1000000000000000D) / (double)(num - 1));
for(int i = 0; i < num; i++)
{
vals[i] = x;
x /= div;
}
}
} else
{
for(int i = 0; i < num; i++)
{
vals[i] = max - ((double)i * (max - min)) / (double)(num - 1);
}
if(vals[num - 1] < 0.01D && vals[num - 2] > 1.0D)
{
vals[num - 1] = 0.0D;
}
}
if(min == max)
{
num = 1;
vals = (new double[] {
min
});
}
}
Graphics2D g = (Graphics2D)img.getGraphics();
Font font = new Font("Dialog", 1, fontSize);
g.setFont(font);
FontMetrics fm = g.getFontMetrics(font);
int height = fm.getHeight() + 2;
NumberFormat nf = ((NumberFormat) (max > 1.0D || mode == 0 ? NumberFormat.getNumberInstance() : ((NumberFormat) (new DecimalFormat()))));
nf.setGroupingUsed(false);
String labels[] = new String[num];
int legendWidth = 0;
for(int i = 0; i < num; i++)
{
if(max < 0.5D || max < 2D && mode == 0)
{
((DecimalFormat)nf).applyPattern("0.#E0");
} else
{
nf.setMaximumFractionDigits(maxFracDigits == -1 ? ((int) (vals[i] < 1.0D ? ((int) (vals[i] < 0.01D ? ((int) (vals[i] < 0.001D ? ((int) (vals[i] <= 0.0001D ? ((int) (vals[i] >= 0.0D ? 5 : 1)) : 4)) : 3)) : 2)) : 1)) : maxFracDigits);
}
labels[i] = className != null && (double)className.length > vals[i] ? className[(int)vals[i]] : nf.format(vals[i]);
int w = fm.stringWidth(labels[i]);
if(w > legendWidth)
{
legendWidth = w;
}
}
computeOffsets(legendWidth + 6 + 2 * height, (num + 2) * height);
for(int i = 0; i < num; i++)
{
int y = getRows() - ((num - i) + 1) * height - yOffset;
g.setColor(new Color(showColor(vals[i], min, max)));
g.fill(new Rectangle(xOffset + 4, y, height, height));
g.setColor(blackandwhite ? Color.black : Color.white);
g.drawString(labels[i], xOffset + 6 + height, (y + height) - 2);
}
}
void computeOffsets(int w, int h)
{
xOffset = 0;
yOffset = 0;
int overlap = computeOverlap(0, 0, w, h);
if(overlap == 0)
{
return;
}
int overlap2 = computeOverlap(getCols() - w, 0, w, h);
if(overlap2 < overlap)
{
overlap = overlap2;
xOffset = getCols() - w;
yOffset = 0;
}
if(overlap == 0)
{
return;
}
overlap2 = computeOverlap(getCols() - w, getRows() - h, w, h);
if(overlap2 < overlap)
{
overlap = overlap2;
xOffset = getCols() - w;
yOffset = getRows() - h;
}
if(overlap == 0)
{
return;
}
overlap2 = computeOverlap(0, getRows() - h, w, h);
if(overlap2 < overlap)
{
overlap = overlap2;
xOffset = 0;
yOffset = getRows() - h;
}
}
int computeOverlap(int llx, int lly, int w, int h)
{
int cnt = 0;
for(int y = lly; y < lly + h; y++)
{
for(int x = llx; x < llx + w; x++)
{
if(nonBackground(getRows() - y, x))
{
cnt++;
}
}
}
return cnt;
}
boolean nonBackground(int r, int c)
{
int i = r * getCols() + c;
return i >= 0 && i < pixels.length && pixels[i] != background;
}
void makeNorth()
{
Graphics2D g = (Graphics2D)img.getGraphics();
g.setColor(blackandwhite ? Color.black : Color.white);
Font font = new Font("Dialog", 1, 48);
g.setFont(font);
FontMetrics fm = g.getFontMetrics(font);
int height = fm.getHeight() + 2;
int nx = (int)((double)getCols() * 0.94999999999999996D);
g.drawString("N", nx, (int)((double)getRows() * 0.10000000000000001D) + height);
int x = nx + fm.stringWidth("N") / 2;
int yb = (int)((double)getRows() * 0.10000000000000001D - (double)height * 1.25D);
int yheight = (int)((double)getRows() * 0.050000000000000003D);
int w = 6;
g.fillRect(x - w / 2, yb, w, yheight);
g.fillPolygon(new int[] {
x, x + 3 * w, x - 3 * w
}, new int[] {
yb - w / 2, yb + 3 * w, yb + 3 * w
}, 3);
}
public void writeImage(String outFile)
{
writeImage(outFile, 1);
}
public void writeImage(String outFile, int magstep)
{
int ncols = getCols();
int nrows = getRows();
BufferedImage toWrite = magstep != 1 ? new BufferedImage(ncols * magstep, nrows * magstep, 1) : img;
if(magstep > 1)
{
int p[] = ((DataBufferInt)toWrite.getRaster().getDataBuffer()).getData();
for(int r = 0; r < nrows * magstep; r++)
{
for(int c = 0; c < ncols * magstep; c++)
{
p[r * ncols * magstep + c] = pixels[(r / magstep) * ncols + c / magstep];
}
}
}
try
{
ImageIO.write(toWrite, "png", new File(outFile));
}
catch(IOException e)
{
System.out.println((new StringBuilder()).append("Error: ").append(e.toString()).toString());
}
}
int showColor(double val, double min, double max)
{
if(mode == 2)
{
return classColor[(int)val];
}
if(mode == 0)
{
val = val > min ? Math.log(val) : Math.log(min);
min = min > 0.0D ? Math.log(min) : 0.0D;
max = max > 0.0D ? Math.log(max) : 0.0D;
}
if(val < min)
{
val = min;
}
if(val > max)
{
val = max;
}
if(dichromatic)
{
if(breakpoint == -9999D)
{
breakpoint = (max - min) / 2D;
}
double frac = val >= breakpoint ? (val - breakpoint) / (max - breakpoint) : (breakpoint - val) / (breakpoint - min);
int end = val >= breakpoint ? 1 : 0;
return fadedColor(dichromaticColors[end], frac);
}
int red;
int green;
int blue;
if(redandyellow)
{
if(breakpoint == -9999D)
{
breakpoint = 50D;
}
int index = (int)(((max - val) * 100D) / (max - min));
red = 255;
green = (int)((double)index >= breakpoint ? 255D : (double)(index * 255) / breakpoint);
blue = (int)((double)index >= breakpoint ? (((double)index - breakpoint) * 255D) / (511D - breakpoint) : 0.0D);
} else
if(blackandwhite)
{
double index = (max - val) / (max - min);
red = green = blue = (int)(220D * index) + 30;
} else
{
int i = (int)(((max - val) * 1020D) / (max - min));
red = i >= 256 ? i <= 510 ? 510 - i : 0 : 255;
green = i >= 256 ? i >= 765 ? 1020 - i : 255 : i;
blue = i >= 510 ? i >= 765 ? 255 : i - 510 : 0;
}
return 0xff000000 | red << 16 | green << 8 | blue;
}
int fadedColor(Color c, double frac)
{
int rgb[] = {
c.getRed(), c.getGreen(), c.getBlue()
};
for(int i = 0; i < 3; i++)
{
rgb[i] = rgb[i] + (int)((1.0D - frac) * (double)(255 - rgb[i]));
}
return (new Color(rgb[0], rgb[1], rgb[2])).getRGB();
}
public void paint(Graphics g)
{
int w = getSize().width;
int h = getSize().height;
if(img != null)
{
g.drawImage(img, 0, 0, w, h, this);
}
}
public void update(Graphics g)
{
paint(g);
}
public Dimension getPreferredSize()
{
return new Dimension(getCols(), getRows());
}
}