update gifaddframe function to support add an BufferedImage

This commit is contained in:
wyq 2024-01-20 09:42:35 +08:00
parent b02bda02f5
commit 97d6f87ce9
28 changed files with 981 additions and 65 deletions

View File

@ -1675,7 +1675,7 @@ public class GLChartPanel extends GLJPanel implements IChartPanel{
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g = image.createGraphics();
paintGraphics(g);
paintGraphics(g, width, height);
return image;
}

View File

@ -24,6 +24,7 @@ import org.meteoinfo.geo.drawing.Draw;
import org.meteoinfo.geo.layer.ImageLayer;
import org.meteoinfo.geo.layer.VectorLayer;
import org.meteoinfo.geo.legend.LegendManage;
import org.meteoinfo.geometry.colors.ExtendType;
import org.meteoinfo.geometry.colors.Normalize;
import org.meteoinfo.geometry.colors.OpacityTransferFunction;
import org.meteoinfo.geometry.colors.TransferFunction;
@ -5014,7 +5015,7 @@ public class GraphicFactory {
* @param isSmooth Is smooth or not
* @return Contour polygons
*/
public static GraphicCollection createContourPolygons(Array xa, Array ya, Array va, LegendScheme ls, boolean isSmooth) {
public static GraphicCollection createContourPolygons_bak(Array xa, Array ya, Array va, LegendScheme ls, boolean isSmooth) {
ls = ls.convertTo(ShapeTypes.POLYGON);
Object[] ccs = LegendManage.getContoursAndColors(ls);
double[] cValues = (double[]) ccs[0];
@ -5133,6 +5134,167 @@ public class GraphicFactory {
return graphics;
}
/**
* Create contour polygons
*
* @param xa X coordinate array - one dimension
* @param ya Y coordinate array - one dimension
* @param va Data array - two dimension
* @param ls Legend scheme
* @param isSmooth Is smooth or not
* @return Contour polygons
*/
public static GraphicCollection createContourPolygons(Array xa, Array ya, Array va, LegendScheme ls, boolean isSmooth) {
double minData = ArrayMath.min(va).doubleValue();
double maxData = ArrayMath.max(va).doubleValue();
ls = ls.convertTo(ShapeTypes.POLYGON);
double[] cValues = ls.getValues(minData, maxData);
int nv = cValues.length;
int[] shape = va.getShape();
int[][] S1 = new int[shape[0]][shape[1]];
double[] x = (double[])ArrayUtil.copyToNDJavaArray_Double(xa);
double[] y = (double[])ArrayUtil.copyToNDJavaArray_Double(ya);
if (x[1] - x[0] < 0) {
ArrayUtils.reverse(x);
va = va.flip(1);
}
if (y[1] - y[0] < 0) {
ArrayUtils.reverse(y);
va = va.flip(0);
}
double missingValue = -9999.0;
double[][] data = (double[][]) ArrayUtil.copyToNDJavaArray_Double(va, missingValue);
Object[] cbs = ContourDraw.tracingContourLines(data,
cValues, x, y, missingValue, S1);
List<PolyLine> contourLines = (List<PolyLine>) cbs[0];
List<wcontour.global.Border> borders = (List<wcontour.global.Border>) cbs[1];
if (isSmooth) {
contourLines = Contour.smoothLines(contourLines);
}
List<wcontour.global.Polygon> contourPolygons = ContourDraw.tracingPolygons(data, contourLines, borders, cValues);
double v, min, max;
ColorBreak cbb = ls.findLegendBreak(0);
ExtendType extendType = ls.getExtendType();
GraphicCollection graphics = new GraphicCollection();
for (int i = 0; i < contourPolygons.size(); i++) {
wcontour.global.Polygon poly = contourPolygons.get(i);
v = poly.LowValue;
int valueIdx = Arrays.binarySearch(cValues, v);
if (valueIdx < 0) {
valueIdx = -valueIdx;
}
if (valueIdx == nv - 1) {
if (poly.IsHighCenter) {
if (maxData > ls.getMaxValue()) {
switch (extendType) {
case NEITHER:
case MIN:
continue;
}
}
min = v;
max = maxData;
} else {
max = v;
min = cValues[valueIdx - 1];
}
} else if (valueIdx == 0){
if (poly.IsHighCenter) {
min = v;
max = cValues[valueIdx + 1];
} else {
if (minData < ls.getMinValue()) {
switch (extendType) {
case NEITHER:
case MAX:
continue;
}
}
max = v;
min = minData;
}
} else {
if (poly.LowValue == poly.HighValue) {
if (poly.IsHighCenter) {
min = v;
max = cValues[valueIdx + 1];
} else {
max = v;
min = cValues[valueIdx - 1];
}
} else {
min = v;
max = poly.HighValue;
}
}
PointD aPoint;
List<PointD> pList = new ArrayList<>();
for (wcontour.global.PointD pointList : poly.OutLine.PointList) {
aPoint = new PointD();
aPoint.X = pointList.X;
aPoint.Y = pointList.Y;
pList.add(aPoint);
}
if (!GeoComputation.isClockwise(pList)) {
Collections.reverse(pList);
}
PolygonShape aPolygonShape = new PolygonShape();
aPolygonShape.setPoints(pList);
aPolygonShape.setExtent(GeometryUtil.getPointsExtent(pList));
aPolygonShape.lowValue = min;
aPolygonShape.highValue = max;
if (poly.HasHoles()) {
for (PolyLine holeLine : poly.HoleLines) {
pList = new ArrayList<>();
for (wcontour.global.PointD pointList : holeLine.PointList) {
aPoint = new PointD();
aPoint.X = pointList.X;
aPoint.Y = pointList.Y;
pList.add(aPoint);
}
aPolygonShape.addHole(pList, 0);
}
}
v = aPolygonShape.lowValue;
switch (ls.getLegendType()) {
case UNIQUE_VALUE:
for (int j = 0; j < ls.getBreakNum(); j++) {
ColorBreak cb = ls.getLegendBreaks().get(j);
if (MIMath.doubleEquals(v, Double.parseDouble(cb.getStartValue().toString()))) {
cbb = cb;
break;
}
}
break;
case GRADUATED_COLOR:
int blNum = 0;
for (int j = 0; j < ls.getBreakNum(); j++) {
ColorBreak cb = ls.getLegendBreaks().get(j);
blNum += 1;
if (MIMath.doubleEquals(v, Double.parseDouble(cb.getStartValue().toString()))
|| (v > Double.parseDouble(cb.getStartValue().toString())
&& v < Double.parseDouble(cb.getEndValue().toString()))
|| (blNum == ls.getBreakNum() && v == Double.parseDouble(cb.getEndValue().toString()))) {
cbb = cb;
break;
}
}
break;
}
graphics.add(new Graphic(aPolygonShape, cbb));
}
graphics.setSingleLegend(false);
graphics.setLegendScheme(ls);
return graphics;
}
/**
* Create 3D contour polygons
*

View File

@ -19,7 +19,6 @@ import java.util.List;
import java.util.Random;
import org.meteoinfo.common.MIMath;
//import org.meteoinfo.legend.LegendManage;
/**
*
@ -30,6 +29,8 @@ public class ColorMap {
private Color[] colors;
private String name = "";
private Color fillColor = Color.white;
private Color overColor;
private Color underColor;
final static int GRADS_RAINBOW = 0;
// </editor-fold>
@ -124,6 +125,54 @@ public class ColorMap {
public void setFillColor(Color value) {
this.fillColor = value;
}
/**
* Get over color
* @return Over color
*/
public Color getOverColor() {
return overColor == null ? colors[colors.length - 1] : overColor;
}
/**
* Set over color
* @param value Over color
*/
public void setOverColor(Color value) {
overColor = value;
}
/**
* Get has over color or not
* @return Has over color or not
*/
public boolean hasOverColor() {
return overColor != null;
}
/**
* Get under color
* @return Under color
*/
public Color getUnderColor() {
return underColor == null ? colors[0] : underColor;
}
/**
* Set under color
* @param value Under color
*/
public void setUnderColor(Color value) {
underColor = value;
}
/**
* Get has under color or not
* @return Has under color or not
*/
public boolean hasUnderColor() {
return underColor != null;
}
// </editor-fold>
// <editor-fold desc="Methods">
@ -163,8 +212,10 @@ public class ColorMap {
* @return Mapped color
*/
public Color map(double value) {
if (value < 0 || value > 1) {
return this.fillColor;
if (value < 0) {
return getUnderColor();
} else if (value > 1) {
return getOverColor();
}
int idx = (int)(value * this.colors.length);

View File

@ -11,7 +11,6 @@ import java.util.List;
*/
public class BoundaryNorm extends Normalize {
private Array boundaries;
private int nColors;
private int size;
private int nRegions; //Number of colors needed
private int offset;
@ -20,17 +19,15 @@ public class BoundaryNorm extends Normalize {
/**
* Constructor
* @param boundaries Boundaries
* @param nColors Number of colors
* @param extendType Extend type
*/
public BoundaryNorm(Array boundaries, int nColors, ExtendType extendType) {
public BoundaryNorm(Array boundaries, ExtendType extendType) {
super();
boundaries = boundaries.copyIfView();
this.setMinValue(boundaries.getDouble(0));
this.setMaxValue(boundaries.getDouble((int)boundaries.getSize() - 1));
this.boundaries = boundaries;
this.nColors = nColors;
this.size = (int) this.boundaries.getSize();
this.extendType = extendType;
this.nRegions = this.size - 1;
@ -50,6 +47,38 @@ public class BoundaryNorm extends Normalize {
}
}
/**
* Get boundaries
* @return Boundaries
*/
public Array getBoundaries() {
return this.boundaries;
}
/**
* Get extend type
* @return Extend type
*/
public ExtendType getExtendType() {
return this.extendType;
}
/**
* Get boundaries size
* @return Boundaries size
*/
public int getSize() {
return this.size;
}
/**
* Get offset
* @return The offset
*/
public int getOffset() {
return this.offset;
}
/**
* Get number of color regions
* @return Number of color regions
@ -59,16 +88,17 @@ public class BoundaryNorm extends Normalize {
}
@Override
public Number apply(double v) {
public Number apply(Number v) {
int idx = ArrayUtil.searchSorted(this.boundaries, v, true) - 1 + this.offset;
return idx;
return (float) idx / this.nRegions;
}
@Override
public Array apply(Array a) {
Array r = ArrayUtil.searchSorted(this.boundaries, a, false);
r = ArrayMath.add(r, this.offset - 1);
r = ArrayMath.div(r, (float) this.nRegions);
return r;
}

View File

@ -4,5 +4,21 @@ public enum ExtendType {
NEITHER,
BOTH,
MIN,
MAX
MAX;
/**
* Get is extend max or not
* @return Is extend max or not
*/
public boolean isExtendMax() {
return this == BOTH || this == MAX;
}
/**
* Get is extend min or not
* @return Is extend min or not
*/
public boolean isExtendMin() {
return this == BOTH || this == MIN;
}
}

View File

@ -37,8 +37,9 @@ public class LogNorm extends Normalize {
}
@Override
public Number apply(double v) {
public Number apply(Number value) {
double range = max - min;
double v = value.doubleValue();
v = Math.log10(v);
v = (v - min) / range;
if (clip) {

View File

@ -126,8 +126,9 @@ public class Normalize {
* @param v The value
* @return Normalized value
*/
public Number apply(double v) {
public Number apply(Number value) {
double range = maxValue - minValue;
double v = value.doubleValue();
v = (v - minValue) / range;
if (clip) {
if (v < 0)

View File

@ -0,0 +1,146 @@
package org.meteoinfo.geometry.colors;
import org.meteoinfo.common.MIMath;
import org.meteoinfo.common.colors.ColorMap;
import org.meteoinfo.geometry.legend.LegendFactory;
import org.meteoinfo.geometry.legend.LegendScheme;
import org.meteoinfo.geometry.shape.ShapeTypes;
import java.awt.*;
public class ScalarMappable {
private ColorMap colorMap;
private Normalize normalize;
/**
* Constructor
*/
public ScalarMappable() {
this.normalize = new Normalize();
this.colorMap = new ColorMap();
}
/**
* Constructor
* @param colorMap Color map
* @param normalize Normalize
*/
public ScalarMappable(ColorMap colorMap, Normalize normalize) {
this.colorMap = colorMap;
this.normalize = normalize;
}
/**
* Constructor
* @param opacityTransferFunction Transfer function
* @param colorMap Color map
*/
public ScalarMappable(ColorMap colorMap) {
this(colorMap, new Normalize());
}
/**
* Get color map
* @return Color map
*/
public ColorMap getColorMap() {
return this.colorMap;
}
/**
* Set color map
* @param value Color map
*/
public void setColorMap(ColorMap value) {
this.colorMap = value;
}
/**
* Get normalize
* @return Normalize
*/
public Normalize getNormalize() {
return this.normalize;
}
/**
* Set normalize
* @param value Normalize
*/
public void setNormalize(Normalize value) {
this.normalize = value;
}
/**
* Get color with a data value
* @param v The data value
* @return The color
*/
public Color getColor(double v) {
float ratio = this.normalize.apply(v).floatValue();
return this.colorMap.map(ratio);
}
/**
* Get colors
* @return Colors
*/
public Color[] getColors() {
if (this.normalize instanceof BoundaryNorm) {
return this.colorMap.getColors(((BoundaryNorm) this.normalize).getNRegions());
} else {
return this.colorMap.getColors();
}
}
/**
* To legend scheme
* @param min The minimum value
* @param max The maximum value
* @param n Legend break number
* @return Legend scheme
*/
public LegendScheme toLegendScheme(double min, double max) {
if (min == max) {
return LegendFactory.createSingleSymbolLegendScheme(ShapeTypes.IMAGE);
}
double[] values = MIMath.getIntervalValues(min, max);
int n = values.length;
Color[] colors = new Color[n + 1];
colors[0] = getColor(min);
for (int i = 1; i < n + 1; i++) {
colors[i] = getColor(values[i - 1]);
}
LegendScheme ls = LegendFactory.createGraduatedLegendScheme(values, colors, ShapeTypes.IMAGE, min, max);
ls.setColorMap(colorMap);
ls.setNormalize(normalize);
return ls;
}
/**
* To legend scheme
* @param min The minimum value
* @param max The maximum value
* @param n Legend break number
* @return Legend scheme
*/
public LegendScheme toLegendScheme(double min, double max, int n) {
if (min == max) {
return LegendFactory.createSingleSymbolLegendScheme(ShapeTypes.IMAGE);
}
double[] values = MIMath.getIntervalValues(min, max, n);
Color[] colors = new Color[n + 1];
colors[0] = getColor(min);
for (int i = 1; i < n + 1; i++) {
colors[i] = getColor(values[i - 1]);
}
LegendScheme ls = LegendFactory.createGraduatedLegendScheme(values, colors, ShapeTypes.IMAGE, min, max);
ls.setColorMap(colorMap);
ls.setNormalize(normalize);
return ls;
}
}

View File

@ -16,10 +16,14 @@ package org.meteoinfo.geometry.legend;
import org.meteoinfo.common.DataConvert;
import org.meteoinfo.common.MIMath;
import org.meteoinfo.common.colors.ColorMap;
import org.meteoinfo.geometry.colors.Normalize;
import org.meteoinfo.geometry.colors.TransferFunction;
import org.meteoinfo.geometry.colors.*;
import org.meteoinfo.geometry.shape.Shape;
import org.meteoinfo.geometry.shape.*;
import org.meteoinfo.ndarray.Array;
import org.meteoinfo.ndarray.IndexIterator;
import org.meteoinfo.ndarray.InvalidRangeException;
import org.meteoinfo.ndarray.math.ArrayMath;
import org.meteoinfo.ndarray.math.ArrayUtil;
import org.meteoinfo.ndarray.util.BigDecimalUtil;
import java.awt.*;
@ -750,6 +754,176 @@ public class LegendManage {
return legendScheme;
}
/**
* Create graduated color legend scheme
*
* @param values The values
* @param colors The colors
* @param shapeType Shape type
* @return The legend scheme
*/
public static LegendScheme createGraduatedLegendScheme(double[] values, Color[] colors, ShapeTypes shapeType) {
int nv = values.length;
double min = values[0];
double max = values[nv - 1];
LegendScheme legendScheme = new LegendScheme(shapeType);
legendScheme.setLegendType(LegendType.GRADUATED_COLOR);
legendScheme.setMinValue(min);
legendScheme.setMaxValue(max);
int i;
if (shapeType.isPoint()) {
for (i = 0; i < colors.length; i++) {
PointBreak aPB = new PointBreak();
aPB.setColor(colors[i]);
aPB.setOutlineColor(Color.black);
aPB.setNoData(false);
aPB.setDrawOutline(true);
aPB.setDrawFill(true);
aPB.setDrawShape(true);
aPB.setStartValue(values[i]);
aPB.setEndValue(values[i + 1]);
aPB.setSize(8);
aPB.setStyle(PointStyle.CIRCLE);
if (aPB.getStartValue() == aPB.getEndValue()) {
aPB.setCaption(DataConvert.removeTailingZeros(aPB.getStartValue().toString()));
} else if (i == 0) {
aPB.setCaption("< " + DataConvert.removeTailingZeros(aPB.getEndValue().toString()));
} else if (i == colors.length - 1) {
aPB.setCaption("> " + DataConvert.removeTailingZeros(aPB.getStartValue().toString()));
} else {
aPB.setCaption(DataConvert.removeTailingZeros(aPB.getStartValue().toString())
+ " - " + DataConvert.removeTailingZeros(aPB.getEndValue().toString()));
}
legendScheme.addLegendBreak(aPB);
}
} else if (shapeType.isLine()) {
for (i = 0; i < colors.length; i++) {
PolylineBreak aPLB = new PolylineBreak();
aPLB.setColor(colors[i]);
aPLB.setWidth(1.0F);
aPLB.setStyle(LineStyles.SOLID);
aPLB.setDrawPolyline(true);
aPLB.setStartValue(values[i]);
aPLB.setEndValue(values[i + 1]);
if (aPLB.getStartValue() == aPLB.getEndValue()) {
aPLB.setCaption(DataConvert.removeTailingZeros(aPLB.getStartValue().toString()));
} else if (i == 0) {
aPLB.setCaption("< " + DataConvert.removeTailingZeros(aPLB.getEndValue().toString()));
} else if (i == colors.length - 1) {
aPLB.setCaption("> " + DataConvert.removeTailingZeros(aPLB.getStartValue().toString()));
} else {
aPLB.setCaption(DataConvert.removeTailingZeros(aPLB.getStartValue().toString())
+ " - " + DataConvert.removeTailingZeros(aPLB.getEndValue().toString()));
}
aPLB.setSymbolColor(aPLB.getColor());
if (i < PointStyle.values().length) {
aPLB.setSymbolStyle(PointStyle.values()[i]);
}
legendScheme.addLegendBreak(aPLB);
}
} else if (shapeType.isPolygon()) {
for (i = 0; i < colors.length; i++) {
PolygonBreak aPGB = new PolygonBreak();
aPGB.setColor(colors[i]);
aPGB.setOutlineColor(Color.gray);
aPGB.setOutlineSize(1.0F);
aPGB.setDrawFill(true);
aPGB.setDrawOutline(false);
aPGB.setDrawShape(true);
aPGB.setStartValue(values[i]);
aPGB.setEndValue(values[i + 1]);
if (aPGB.getStartValue() == aPGB.getEndValue()) {
aPGB.setCaption(DataConvert.removeTailingZeros(aPGB.getStartValue().toString()));
} else if (i == 0) {
aPGB.setCaption("< " + DataConvert.removeTailingZeros(aPGB.getEndValue().toString()));
} else if (i == colors.length - 1) {
aPGB.setCaption("> " + DataConvert.removeTailingZeros(aPGB.getStartValue().toString()));
} else {
aPGB.setCaption(DataConvert.removeTailingZeros(aPGB.getStartValue().toString())
+ " - " + DataConvert.removeTailingZeros(aPGB.getEndValue().toString()));
}
legendScheme.addLegendBreak(aPGB);
}
} else {
for (i = 0; i < colors.length; i++) {
ColorBreak aCB = new ColorBreak();
aCB.setColor(colors[i]);
aCB.setStartValue(values[i]);
aCB.setEndValue(values[i + 1]);
if (aCB.getStartValue() == aCB.getEndValue()) {
aCB.setCaption(DataConvert.removeTailingZeros(aCB.getStartValue().toString()));
} else if (i == 0) {
aCB.setCaption("< " + DataConvert.removeTailingZeros(aCB.getEndValue().toString()));
} else if (i == colors.length - 1) {
aCB.setCaption("> " + DataConvert.removeTailingZeros(aCB.getStartValue().toString()));
} else {
aCB.setCaption(DataConvert.removeTailingZeros(aCB.getStartValue().toString())
+ " - " + DataConvert.removeTailingZeros(aCB.getEndValue().toString()));
}
legendScheme.addLegendBreak(aCB);
}
}
return legendScheme;
}
/**
* Create graduated color legend scheme
*
* @param norm The BoundaryNorm
* @param colorMap The color map
* @return The legend scheme
*/
public static LegendScheme createGraduatedLegendScheme(BoundaryNorm norm, ColorMap colorMap) {
Array values = norm.getBoundaries();
int nv = (int) values.getSize();
double min = values.getDouble(0);
double max = values.getDouble(nv - 1);
LegendScheme legendScheme = new LegendScheme(ShapeTypes.IMAGE);
legendScheme.setLegendType(LegendType.GRADUATED_COLOR);
legendScheme.setMinValue(min);
legendScheme.setMaxValue(max);
Color[] colors = colorMap.getColors(norm.getNRegions());
ExtendType extendType = norm.getExtendType();
legendScheme.setExtendType(extendType);
int offset = norm.getOffset();
for (int i = 0; i < nv - 1; i++) {
ColorBreak cb = new ColorBreak();
cb.setColor(colors[i + offset]);
cb.setStartValue(values.getDouble(i));
cb.setEndValue(values.getDouble(i + 1));
cb.setCaption(DataConvert.removeTailingZeros(cb.getStartValue().toString())
+ " - " + DataConvert.removeTailingZeros(cb.getEndValue().toString()));
legendScheme.addLegendBreak(cb);
}
if (extendType.isExtendMin()) {
ColorBreak cb = new ColorBreak();
cb.setColor(colors[0]);
cb.setStartValue(Double.MIN_VALUE);
cb.setEndValue(values.getDouble(0));
cb.setCaption("< " + DataConvert.removeTailingZeros(cb.getEndValue().toString()));
legendScheme.addLegendBreak(0, cb);
}
if (extendType.isExtendMax()) {
ColorBreak cb = new ColorBreak();
cb.setColor(colors[colors.length - 1]);
cb.setStartValue(values.getDouble(nv - 1));
cb.setEndValue(Double.MAX_VALUE);
cb.setCaption("> " + DataConvert.removeTailingZeros(cb.getStartValue().toString()));
legendScheme.addLegendBreak(cb);
}
return legendScheme;
}
/**
* Create legend scheme
* @param values Value list
@ -899,6 +1073,26 @@ public class LegendManage {
return createLegendScheme(min, max, values, colors, LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, false, -9999.0);
}
/**
* Create legend scheme
*
* @param min Minimum
* @param max Maximum
* @param ct Color table
* @param extend Extend min/max values or not
* @return LegendScheme
*/
public static LegendScheme createLegendScheme(double min, double max, ColorMap ct, boolean extend) {
double[] values = (double[]) MIMath.getIntervalValues(min, max, extend).get(0);
if (extend) {
Color[] colors = ct.getColors(values.length - 1);
return createLegendScheme(values, colors);
} else {
Color[] colors = ct.getColors(values.length + 1);
return createLegendScheme(min, max, values, colors, LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, false, -9999.0);
}
}
/**
* Create legend scheme
*
@ -1013,6 +1207,17 @@ public class LegendManage {
return ls;
}
/**
* Create legend scheme
*
* @param values Values
* @param colors Colors
* @return Legend scheme
*/
public static LegendScheme createLegendScheme(double[] values, Color[] colors) {
return createGraduatedLegendScheme(values, colors, ShapeTypes.IMAGE);
}
/**
* Create legend scheme
*
@ -1101,6 +1306,54 @@ public class LegendManage {
return createLegendScheme(min, max, values, colors, LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, false, -9999.0);
}
/**
* Create legend scheme
*
* @param min Minimum
* @param max Maximum
* @param levels Level values
* @param ct Color table
* @param extend Extend min/max values or not
* @return LegendScheme
*/
public static LegendScheme createLegendScheme(double min, double max, List<Number> levels,
ColorMap ct, boolean extend) {
if (levels.size() == ct.getColorCount()){
return createUniqValueLegendScheme(levels, ct.getColors(), ShapeTypes.IMAGE);
}
double[] values = new double[levels.size()];
for (int i = 0; i < levels.size(); i++) {
values[i] = levels.get(i).doubleValue();
}
if (extend) {
Color[] colors = ct.getColors(values.length - 1);
return createLegendScheme(values, colors);
} else {
Color[] colors = ct.getColors(levels.size() + 1);
return createLegendScheme(min, max, values, colors, LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, false, -9999.0);
}
}
/**
* Create legend scheme
*
* @param levels Level values
* @param colorMap Color map
* @param extendType ExtendType
* @return LegendScheme Legend scheme
*/
public static LegendScheme createLegendScheme(List<Number> levels, ColorMap colorMap,
ExtendType extendType) {
Array boundaries = ArrayUtil.array(levels);
BoundaryNorm norm = new BoundaryNorm(boundaries, extendType);
return createGraduatedLegendScheme(norm, colorMap);
}
/**
* Create legend scheme
*
@ -1111,10 +1364,31 @@ public class LegendManage {
* @return LegendScheme
*/
public static LegendScheme createLegendScheme(double min, double max, int n, ColorMap ct) {
double[] values = MIMath.getIntervalValues(min, max, n);
Color[] colors = ct.getColors(values.length + 1);
return createLegendScheme(min, max, n, ct, false);
}
return createLegendScheme(min, max, values, colors, LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, false, -9999.0);
/**
* Create legend scheme
*
* @param min Minimum
* @param max Maximum
* @param n Level number
* @param ct Color table
* @param extend Extend min/max values or not
* @return LegendScheme
*/
public static LegendScheme createLegendScheme(double min, double max, int n, ColorMap ct, boolean extend) {
if (extend) {
double[] values = MIMath.getIntervalValues(min, max, n);
Color[] colors = ct.getColors(values.length - 1);
return createLegendScheme(values, colors);
} else {
double[] values = MIMath.getIntervalValues(min, max, n);
Color[] colors = ct.getColors(values.length + 1);
return createLegendScheme(min, max, values, colors, LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, false, -9999.0);
}
}
/**
@ -1137,6 +1411,89 @@ public class LegendManage {
return ls;
}
/**
* Create image legend from array data
*
* @param array Array data
* @param colorMap Color map
* @return Legend scheme
*/
public static LegendScheme createImageLegend(Array array, ColorMap colorMap) {
boolean isUnique = ArrayUtil.isUnique(array, 20);
LegendScheme ls;
if (isUnique) {
try {
Array ua = ArrayUtil.unique(array, null);
List<Number> values = new ArrayList<>();
IndexIterator iter = ua.getIndexIterator();
while (iter.hasNext()) {
values.add((Number) iter.getObjectNext());
}
ls = LegendManage.createUniqValueLegendScheme(values, colorMap, ShapeTypes.IMAGE);
} catch (InvalidRangeException e) {
throw new RuntimeException(e);
}
} else if (ArrayMath.containsNaN(array)) {
ls = LegendManage.createLegendScheme(ArrayMath.min(array).doubleValue(),
ArrayMath.max(array).doubleValue(), colorMap, Double.NaN);
} else {
ls = LegendManage.createLegendScheme(ArrayMath.min(array).doubleValue(),
ArrayMath.max(array).doubleValue(), colorMap);
}
return ls;
}
/**
* Create image legend from grid data
*
* @param array Array data
* @param n Legend break number
* @param colorMap Color map
* @return Legend scheme
*/
public static LegendScheme createImageLegend(Array array, int n, ColorMap colorMap) {
LegendScheme ls;
double min = ArrayMath.min(array).doubleValue();
double max = ArrayMath.max(array).doubleValue();
if (ArrayMath.containsNaN(array)) {
ls = LegendManage.createLegendScheme(min, max, n, colorMap,
LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, true, Double.NaN);
} else {
ls = LegendManage.createLegendScheme(min, max, n, colorMap,
LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, false, Double.NaN);
}
return ls;
}
/**
* Create image legend from array data
*
* @param array Array data
* @param levels Legend break values
* @param colorMap Color map
* @return Legend scheme
*/
public static LegendScheme createImageLegend(Array array, List<Number> levels, ColorMap colorMap) {
LegendScheme ls;
if (colorMap.getColorCount() == levels.size()){
ls = LegendManage.createUniqValueLegendScheme(levels, colorMap, ShapeTypes.IMAGE);
} else {
double min = ArrayMath.min(array).doubleValue();
double max = ArrayMath.max(array).doubleValue();
if (ArrayMath.containsNaN(array)) {
ls = LegendManage.createLegendScheme(min, max, levels, colorMap,
LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, true, Double.NaN);
} else {
ls = LegendManage.createLegendScheme(min, max, levels, colorMap,
LegendType.GRADUATED_COLOR, ShapeTypes.IMAGE, false, Double.NaN);
}
}
return ls;
}
/**
* Create random colors
*

View File

@ -13,11 +13,10 @@
*/
package org.meteoinfo.geometry.legend;
//import org.meteoinfo.data.meteodata.DrawType2D;
import org.meteoinfo.common.DataConvert;
import org.meteoinfo.common.colors.ColorMap;
import org.meteoinfo.common.colors.ColorUtil;
import org.meteoinfo.geometry.colors.ExtendType;
import org.meteoinfo.geometry.colors.Normalize;
import org.meteoinfo.geometry.shape.ShapeTypes;
import org.w3c.dom.*;
@ -50,6 +49,7 @@ package org.meteoinfo.geometry.legend;
private String fieldName = "";
private LegendType legendType = LegendType.SINGLE_SYMBOL;
private ShapeTypes shapeType;
private ExtendType extendType = ExtendType.NEITHER;
private List<ColorBreak> legendBreaks;
private boolean hasNoData;
private double minValue;
@ -204,6 +204,30 @@ package org.meteoinfo.geometry.legend;
shapeType = st;
}
/**
* Get extend type
* @return Extend type
*/
public ExtendType getExtendType() {
return extendType;
}
/**
* Set extend type
* @param value Extend type
*/
public void setExtendType(ExtendType value) {
extendType = value;
}
/**
* Set extend type
* @param value Extend type
*/
public void setExtendType(String value) {
extendType = ExtendType.valueOf(value.toUpperCase());
}
/**
* Get break type
*
@ -455,6 +479,18 @@ package org.meteoinfo.geometry.legend;
this.updateUniqueValueMap();
}
/**
* Add a legend break by index
*
* @param index The index
* @param lb Legend break
*/
public void addLegendBreak(int index, ColorBreak lb){
this.legendBreaks.add(index, lb);
if (this.legendType == LegendType.UNIQUE_VALUE)
this.updateUniqueValueMap();
}
/**
* Add a legend breaks
* @param lb Legend breaks
@ -575,6 +611,45 @@ package org.meteoinfo.geometry.legend;
return vs;
}
/**
* Get legend values
* @return Legend values
*/
public double[] getValues(double min, double max) {
List<Double> values = new ArrayList<>();
ColorBreak cb;
double v = 0;
for (int i = 0; i < legendBreaks.size(); i++) {
cb = legendBreaks.get(i);
if (!cb.isNoData()) {
if (values.isEmpty()) {
if (this.legendType == LegendType.UNIQUE_VALUE) {
v = Double.parseDouble(cb.getEndValue().toString());
} else {
v = Double.parseDouble(cb.getStartValue().toString());
if (v >= min && v <= max) {
values.add(v);
}
v = Double.parseDouble(cb.getEndValue().toString());
}
} else {
v = Double.parseDouble(cb.getEndValue().toString());
}
if (v >= min && v <= max) {
values.add(v);
}
}
}
double[] vs = new double[values.size()];
for (int i = 0; i < values.size(); i++) {
vs[i] = values.get(i);
}
return vs;
}
/**
* Get color list
*
@ -709,6 +784,7 @@ package org.meteoinfo.geometry.legend;
}
LegendScheme ls = new LegendScheme(shapeType);
ls.extendType = this.extendType;
ls.fieldName = this.fieldName;
ls.hasNoData = this.hasNoData;
ls.legendType = this.legendType;
@ -781,6 +857,7 @@ package org.meteoinfo.geometry.legend;
}
LegendScheme ls = new LegendScheme(shapeType);
ls.extendType = this.extendType;
ls.fieldName = this.fieldName;
ls.hasNoData = this.hasNoData;
ls.legendType = this.legendType;

View File

@ -148,7 +148,7 @@ public class AnimatedGifEncoder {
getImagePixels(); // convert to correct format if necessary
analyzePixels(); // build color table & map pixels
if (firstFrame) {
writeLSD(); // logical screen descriptior
writeLSD(); // logical screen description
writePalette(); // global color table
if (repeat >= 0) {
// use NS app extension to indicate reps
@ -170,7 +170,7 @@ public class AnimatedGifEncoder {
}
/**
* Flushes any pending data and closes output file.If writing to an
* Flushes any pending data and closes output file. If writing to an
* OutputStream, the stream is not closed.
* @return
*/

View File

@ -1,32 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MeteoInfo File="milconfig.xml" Type="configurefile">
<Path OpenPath="D:\Working\MIScript\Jython\mis\io\json">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\netcdf"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo\wrf"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\ascii"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\3d_earth"/>
<RecentFolder Folder="D:\Working\MIScript\Jython"/>
<RecentFolder Folder="D:\Working\MIScript"/>
<Path OpenPath="D:\Working\MIScript\Jython\mis\common_math\interpolate">
<RecentFolder Folder="D:\Working\MIScript\cuace_dust"/>
<RecentFolder Folder="D:\Working\MIScript\cuace_dust\py"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\traj"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\json"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\contour"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\image"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map\webmap"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo\wrf"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math\interpolate"/>
</Path>
<File>
<OpenedFiles>
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\json\test_geojson_file_read_write.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\traj\hy_conc_geojson.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\json\hy_conc_nuclear_geojson.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\contour\contourf_extend_1.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\contour\contourf_extend.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\common_math\interpolate\interp2d_1.py"/>
</OpenedFiles>
<RecentFiles>
<RecentFile File="D:\Working\MIScript\Jython\mis\io\json\test_geojson_file_read_write.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\traj\hy_conc_geojson.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\io\json\hy_conc_nuclear_geojson.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\contour\contourf_extend_1.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\contour\contourf_extend.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\common_math\interpolate\interp2d_1.py"/>
</RecentFiles>
</File>
<Font>
@ -34,5 +36,5 @@
</Font>
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
<Figure DoubleBuffering="true"/>
<Startup MainFormLocation="-7,0" MainFormSize="1330,805"/>
<Startup MainFormLocation="-7,0" MainFormSize="1370,786"/>
</MeteoInfo>

View File

@ -818,7 +818,8 @@ class DimDataFiles(list):
return TDimVariable(self[0].dataset.getDataInfo().getVariable(key), self)
else:
return list.__getitem__(self, key)
@property
def filenames(self):
"""
Get file names.
@ -907,11 +908,12 @@ class DimDataFiles(list):
:returns: (*datetime*) The time
"""
return self.times[idx]
@property
def varnames(self):
"""
Get variable names
"""
return self[0].varnames()
return self[0].varnames
#############################################

View File

@ -110,7 +110,7 @@ def gifanimation(filename, repeat=0, delay=1000):
def gifaddframe(animation, dpi=None):
"""
Add a frame to an gif animation object
Add a frame to a gif animation object
:param animation: Gif animation object
:param dpi: (*int*) Image resolution
@ -136,7 +136,7 @@ def gifwrite(imfns, giffn, repeat=0, delay=1000):
:param imfns: (*list*) Input image file names.
:param giffn: (*string*) Output gif file name.
:param: repeat: (*int, Default 0*) Animation repeat time number. 0 means repeat forever.
:param: delay: (*int, Default 1000*) Animation frame delay time with units of millsecond.
:param: delay: (*int, Default 1000*) Animation frame delay time with units of millisecond.
"""
ImageUtil.createGifAnimator(imfns, giffn, delay, repeat)

View File

@ -12,11 +12,12 @@ from org.meteoinfo.chart.plot import Plot2D, PolarPlot, PlotOrientation
from org.meteoinfo.chart.graphic import GraphicFactory
from org.meteoinfo.common import XAlign, YAlign
from org.meteoinfo.chart.axis import Axis, LonLatAxis, TimeAxis, LogAxis
from org.meteoinfo.geo.legend import LegendManage
#from org.meteoinfo.geo.legend import LegendManage
from org.meteoinfo.geometry.legend import BarBreak, PolygonBreak, PolylineBreak, \
PointBreak, LineStyles, PointStyle, LegendScheme, LegendType
PointBreak, LineStyles, PointStyle, LegendScheme, LegendType, LegendManage
from org.meteoinfo.geometry.shape import ShapeTypes
from org.meteoinfo.geometry.graphic import Graphic, GraphicCollection
from org.meteoinfo.geometry.colors import ExtendType
from org.meteoinfo.common import MIMath, Extent
from org.meteoinfo.geo.layer import MapLayer
@ -2442,6 +2443,10 @@ class Axes(object):
string, like r or red, all levels will be plotted in this color. If a tuple of matplotlib
color args (string, float, rgb, etc.), different levels will be plotted in different colors in
the order specified.
:param extend: (*string*) {'neither', 'both', 'min', 'max'}, default: 'neither'. Determines the
contourf-coloring of values that are outside the levels range. If 'neither', values outside
the levels range are not colored. If 'min', 'max' or 'both', color the values below, above
or below and above the levels range.
:param smooth: (*boolean*) Smooth contour lines or not.
:returns: (*VectoryLayer*) Contour filled VectoryLayer created from array data.
@ -2468,18 +2473,19 @@ class Axes(object):
vmin = kwargs.pop('vmin', a.min())
vmax = kwargs.pop('vmax', a.max())
extend = ExtendType.valueOf(kwargs.pop('extend', 'neither').upper())
if ls is None:
if len(args) > 0:
level_arg = args[0]
if isinstance(level_arg, int):
cn = level_arg
ls = LegendManage.createLegendScheme(vmin, vmax, cn, cmap)
ls = LegendManage.createLegendScheme(vmin, vmax, cn, cmap, True)
else:
if isinstance(level_arg, NDArray):
level_arg = level_arg.aslist()
ls = LegendManage.createLegendScheme(vmin, vmax, level_arg, cmap)
ls = LegendManage.createLegendScheme(level_arg, cmap, extend)
else:
ls = LegendManage.createLegendScheme(vmin, vmax, cmap)
ls = LegendManage.createLegendScheme(vmin, vmax, cmap, True)
ls = ls.convertTo(ShapeTypes.POLYGON)
if 'edgecolor' not in kwargs.keys():
kwargs['edgecolor'] = None
@ -2517,7 +2523,7 @@ class Axes(object):
:param cmap: (*string*) Color map string.
:param colors: (*list*) If None (default), the colormap specified by cmap will be used. If a
string, like r or red, all levels will be plotted in this color. If a tuple of matplotlib
color args (string, float, rgb, etc), different levels will be plotted in different colors in
color args (string, float, rgb, etc.), different levels will be plotted in different colors in
the order specified.
:param interpolation: (*string*) Interpolation option [None | bilinear | bicubic].
@ -2607,7 +2613,7 @@ class Axes(object):
def pcolor(self, *args, **kwargs):
"""
Draw a pseudocolor plot.
Draw a pseudo color plot.
:param x: (*array_like*) Optional. X coordinate array.
:param y: (*array_like*) Optional. Y coordinate array.
@ -2712,7 +2718,7 @@ class Axes(object):
:param coordinates=['axes'|'figure'|'data'|'inches']: (*string*) Coordinate system and units for
*X, Y*. 'axes' and 'figure' are normalized coordinate system with 0,0 in the lower left and
1,1 in the upper right, 'data' are the axes data coordinates (Default value); 'inches' is
position in the figure in inches, with 0,0 at the lower left corner.
position in the figure in inches, with 0,0 in the lower left corner.
"""
ctext = plotutil.text(x, y, s, **kwargs)
self._axes.addText(ctext)

View File

@ -777,3 +777,19 @@ class Figure(ChartPanel):
if not symbol is None:
self.getChart().setSymbolAntialias(symbol)
def get_image(self):
"""
Get view image.
:return: (*BufferedImage*) Current view image.
"""
return self.getViewImage()
def draw_image(self):
"""
Draw view image.
:return: (*BufferedImage*) View image.
"""
return self.paintViewImage()

View File

@ -785,3 +785,19 @@ class GLFigure(GLChartPanel):
if not symbol is None:
self.getChart().setSymbolAntialias(symbol)
def get_image(self):
"""
Get view image.
:return: (*BufferedImage*) Current view image.
"""
return self.getViewImage()
def draw_image(self):
"""
Draw view image.
:return: (*BufferedImage*) View image.
"""
return self.paintViewImage()

View File

@ -168,4 +168,4 @@ class BoundaryNorm(Normalize):
boundaries = np.array(boundaries)
extend = ExtendType.valueOf(extend.upper())
self._norm = JBoundaryNorm(boundaries._array, ncolors, extend)
self._norm = JBoundaryNorm(boundaries._array, ncolors, extend)

View File

@ -2329,7 +2329,7 @@ def gifanimation(filename, repeat=0, delay=1000):
return encoder
def gifaddframe(animation, width=None, height=None, dpi=None):
def gifaddframe(animation, width=None, height=None, dpi=None, image=None):
"""
Add a frame to a gif animation object
@ -2337,18 +2337,22 @@ def gifaddframe(animation, width=None, height=None, dpi=None):
:param width: (*int*) Image width
:param height: (*int*) Image height
:param dpi: (*int*) Image resolution
:param image: (*image*) The image, default is None means the image will be created from the current
figure.
"""
# chartpanel.paintGraphics()
if dpi is None:
if width is None or height is None:
animation.addFrame(g_figure.paintViewImage())
if image is None:
if dpi is None:
if width is None or height is None:
animation.addFrame(g_figure.paintViewImage())
else:
animation.addFrame(g_figure.paintViewImage(width, height))
else:
animation.addFrame(g_figure.paintViewImage(width, height))
if width is None or height is None:
animation.addFrame(g_figure.paintViewImage(dpi))
else:
animation.addFrame(g_figure.paintViewImage(width, height, dpi))
else:
if width is None or height is None:
animation.addFrame(g_figure.paintViewImage(dpi))
else:
animation.addFrame(g_figure.paintViewImage(width, height, dpi))
animation.addFrame(image)
def giffinish(animation):

View File

@ -112,6 +112,8 @@ def getcolor(style, alpha=None):
c = Color.gray
elif style == 'lightgray':
c = Color.lightGray
elif style == 'darkgray':
c = Color.darkGray
elif style == 'cyan' or style == 'c':
c = Color.cyan
elif style == 'magenta' or style == 'm':
@ -601,6 +603,9 @@ def getlegendscheme(args, min, max, **kwargs):
def setlegendscheme(ls, **kwargs):
extend = kwargs.pop('extend', None)
if extend is not None:
ls.setExtendType(extend)
st = ls.getShapeType()
if st == ShapeTypes.POINT:
setlegendscheme_point(ls, **kwargs)
@ -968,7 +973,7 @@ def text(x, y, s, **kwargs):
:param coordinates=['axes'|'figure'|'data'|'inches']: (*string*) Coordinate system and units for
*X, Y*. 'axes' and 'figure' are normalized coordinate system with 0,0 in the lower left and
1,1 in the upper right, 'data' are the axes data coordinates (Default value); 'inches' is
position in the figure in inches, with 0,0 at the lower left corner.
position in the figure in inches, with 0,0 in the lower left corner.
:returns: (*ChartText*) text.
"""

View File

@ -2364,6 +2364,30 @@ public class ArrayUtil {
}
}
/**
* Check array is unique by unique value number
* @param a The array
* @param limit The limit unique value number
* @return Is unique or not
*/
public static boolean isUnique(Array a, int limit) {
IndexIterator iter = a.getIndexIterator();
List<Object> values = new ArrayList<>();
int n = 0;
Object v;
while (iter.hasNext()) {
v = iter.getObjectNext();
if (!values.contains(v)) {
values.add(v);
}
if (values.size() > limit) {
return false;
}
}
return true;
}
/**
* Find the unique elements and index of an array.
*