Issue #2287 Fix scaling and direction of wind/swell arrows in GFE
Change-Id: Id12823256fc3c383005098b83e72249284a36f33 Former-commit-id: b0c88cb7d71cf3e081596cd47d88d8412c2ea3d1
This commit is contained in:
parent
f5c3b89dd2
commit
77db16933e
17 changed files with 763 additions and 262 deletions
|
@ -57,15 +57,17 @@ import de.micromata.opengis.kml.v_2_2_0.Style;
|
|||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jun 1, 2012 bsteffen Initial creation
|
||||
* Jun 1, 2012 bsteffen Initial creation
|
||||
* Aug 27, 2013 #2287 randerso Removed 180 degree adjustment required by error
|
||||
* in Maputil.rotation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bsteffen
|
||||
* @version 1.0
|
||||
*/
|
||||
public class KmlPointImageExtension extends GraphicsExtension<KmlGraphicsTarget>
|
||||
implements IPointImageExtension {
|
||||
public class KmlPointImageExtension extends
|
||||
GraphicsExtension<KmlGraphicsTarget> implements IPointImageExtension {
|
||||
private static final transient IUFStatusHandler statusHandler = UFStatus
|
||||
.getHandler(KmlRasterImageExtension.class);
|
||||
|
||||
|
@ -113,7 +115,7 @@ public class KmlPointImageExtension extends GraphicsExtension<KmlGraphicsTarget>
|
|||
|
||||
IconStyle iconStyle = style.createAndSetIconStyle();
|
||||
iconStyle.setScale(options.getPlotIconScale());
|
||||
double heading = 180 + MapUtil.rotation(
|
||||
double heading = MapUtil.rotation(
|
||||
new com.vividsolutions.jts.geom.Coordinate(loc
|
||||
.getLongitude(), loc.getLatitude()),
|
||||
gridGeometry);
|
||||
|
|
|
@ -79,6 +79,7 @@ import com.raytheon.uf.viz.core.style.VizStyleException;
|
|||
import com.raytheon.uf.viz.core.style.level.SingleLevel;
|
||||
import com.raytheon.viz.core.contours.rsc.displays.GriddedContourDisplay;
|
||||
import com.raytheon.viz.core.contours.rsc.displays.GriddedVectorDisplay;
|
||||
import com.raytheon.viz.core.contours.util.VectorGraphicsRenderableFactory;
|
||||
import com.raytheon.viz.core.drawables.ColorMapParameterFactory;
|
||||
import com.raytheon.viz.core.rsc.displays.GriddedImageDisplay;
|
||||
import com.raytheon.viz.core.rsc.displays.GriddedImageDisplay.GriddedImagePaintProperties;
|
||||
|
@ -93,8 +94,10 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
* SOFTWARE HISTORY
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Nov 5, 2009 randerso Initial creation
|
||||
* Jan 8, 2010 4205 jelkins add equals checking for OA resources
|
||||
* Nov 5, 2009 randerso Initial creation
|
||||
* Jan 8, 2010 4205 jelkins add equals checking for OA resources
|
||||
* Aug 27, 2013 2287 randerso Added new parameters to GriddedVectorDisplay
|
||||
* constructor
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -424,9 +427,10 @@ public class OAResource extends
|
|||
FloatBuffer mag = data;
|
||||
data.position(transformer.getNx() * transformer.getNy());
|
||||
FloatBuffer dir = data.slice();
|
||||
VectorGraphicsRenderableFactory factory = new VectorGraphicsRenderableFactory();
|
||||
GriddedVectorDisplay vector = new GriddedVectorDisplay(mag,
|
||||
dir, descriptor, transformer.getGridGeom(), 80,
|
||||
displayType);
|
||||
dir, descriptor, transformer.getGridGeom(), 80, 0.75,
|
||||
true, displayType, factory);
|
||||
|
||||
renderableMap.put(dataTime, vector);
|
||||
break;
|
||||
|
|
|
@ -65,6 +65,9 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 23, 2010 bsteffen Initial creation
|
||||
* Aug 27, 2013 #2287 randerso Replaced hard coded constant with densityFactor
|
||||
* parameter to allow application specific density
|
||||
* scaling to better match A1 displays
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -111,6 +114,8 @@ public abstract class AbstractGriddedDisplay<T> implements IRenderable {
|
|||
|
||||
protected final int size;
|
||||
|
||||
protected final double densityFactor;
|
||||
|
||||
protected RGB color;
|
||||
|
||||
protected double density = 1.0;
|
||||
|
@ -128,16 +133,18 @@ public abstract class AbstractGriddedDisplay<T> implements IRenderable {
|
|||
* @param descriptor
|
||||
* @param gridGeometryOfGrid
|
||||
* @param size
|
||||
* @param plotLocations
|
||||
* Pre-configured plot locations. If null, they will be created.
|
||||
* @param densityFactor
|
||||
* adjustment factor to make density match A1
|
||||
*/
|
||||
public AbstractGriddedDisplay(IMapDescriptor descriptor,
|
||||
GeneralGridGeometry gridGeometryOfGrid, int size) {
|
||||
GeneralGridGeometry gridGeometryOfGrid, int size,
|
||||
double densityFactor) {
|
||||
this.calculationQueue = new ConcurrentLinkedQueue<Coordinate>();
|
||||
|
||||
this.descriptor = descriptor;
|
||||
this.gridGeometryOfGrid = gridGeometryOfGrid;
|
||||
this.size = size;
|
||||
this.densityFactor = densityFactor;
|
||||
|
||||
this.gridDims = new int[] {
|
||||
this.gridGeometryOfGrid.getGridRange().getSpan(0),
|
||||
|
@ -233,7 +240,8 @@ public abstract class AbstractGriddedDisplay<T> implements IRenderable {
|
|||
|
||||
List<GridCellRenderable> renderables = new ArrayList<GridCellRenderable>();
|
||||
int increment = Math.max(
|
||||
(int) Math.ceil(adjSize * 0.75 / pixelSize / density), 1);
|
||||
(int) Math.ceil(adjSize * densityFactor / pixelSize / density),
|
||||
1);
|
||||
for (int x = 0; x < gridDims[0]; x += increment) {
|
||||
for (int y = 0; y < gridDims[1]; y += increment) {
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.raytheon.uf.viz.core.drawables.PaintProperties;
|
|||
import com.raytheon.uf.viz.core.exception.VizException;
|
||||
import com.raytheon.uf.viz.core.map.IMapDescriptor;
|
||||
import com.raytheon.uf.viz.core.rsc.DisplayType;
|
||||
import com.raytheon.viz.core.contours.util.IVectorGraphicsRenderableFactory;
|
||||
import com.raytheon.viz.core.contours.util.VectorGraphicsRenderable;
|
||||
import com.vividsolutions.jts.geom.Coordinate;
|
||||
|
||||
|
@ -53,6 +54,13 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
* Jun 22, 2010 bsteffen Initial creation
|
||||
* Feb 07, 2011 7948 bkowal added a public method to get
|
||||
* the direction.
|
||||
* Aug 27, 2013 2287 randerso Added VectorGraphicsRenderable Factory to allow
|
||||
* application specific rendering of wind barbs and
|
||||
* arrows.
|
||||
* Added densityFactor to allow application specific
|
||||
* adjustment of density.
|
||||
* Added gridRelative flag to indicate whether direction
|
||||
* data is relative to grid or true north
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -69,36 +77,43 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
|
||||
private LineStyle lineStyle;
|
||||
|
||||
private double scale = 0.6;
|
||||
|
||||
private IExtent lastExtent;
|
||||
|
||||
private VectorGraphicsRenderable vectorRenderable;
|
||||
|
||||
private boolean gridRelative;
|
||||
|
||||
private DisplayType displayType;
|
||||
|
||||
private GeodeticCalculator gc;
|
||||
|
||||
private IVectorGraphicsRenderableFactory factory;
|
||||
|
||||
/**
|
||||
* @param magnitude
|
||||
* @param direction
|
||||
* @param mode
|
||||
* @param descriptor
|
||||
* @param gridGeometryOfGrid
|
||||
* @param imageSize
|
||||
* @param gridLocation
|
||||
* @param forceCircle
|
||||
* @param plotLocations
|
||||
* Pre-configured plot locations. If null, they will be created.
|
||||
* @param size
|
||||
* @param densityFactor
|
||||
* adjustment factor to make density match A1
|
||||
* @param gridRelative
|
||||
* true if direction is grid relative, false if relative to true
|
||||
* north
|
||||
* @param displayType
|
||||
* @param factory
|
||||
*/
|
||||
public GriddedVectorDisplay(FloatBuffer magnitude, FloatBuffer direction,
|
||||
IMapDescriptor descriptor, GeneralGridGeometry gridGeometryOfGrid,
|
||||
int size, DisplayType displayType) {
|
||||
super(descriptor, gridGeometryOfGrid, size);
|
||||
int size, double densityFactor, boolean gridRelative,
|
||||
DisplayType displayType, IVectorGraphicsRenderableFactory factory) {
|
||||
super(descriptor, gridGeometryOfGrid, size, densityFactor);
|
||||
this.magnitude = magnitude;
|
||||
this.direction = direction;
|
||||
this.gridRelative = gridRelative;
|
||||
this.displayType = displayType;
|
||||
this.gc = new GeodeticCalculator(descriptor.getCRS());
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,8 +125,8 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
lastExtent = paintProps.getView().getExtent().clone();
|
||||
}
|
||||
if (vectorRenderable == null) {
|
||||
vectorRenderable = new VectorGraphicsRenderable(descriptor, target,
|
||||
this.size, this.scale);
|
||||
vectorRenderable = factory.createRenderable(descriptor, target,
|
||||
this.size);
|
||||
super.paint(target, paintProps);
|
||||
}
|
||||
vectorRenderable.setColor(this.color);
|
||||
|
@ -126,12 +141,13 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
super.issueRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paint(Coordinate ijcoord, PaintProperties paintProps,
|
||||
Coordinate plotLoc, double adjSize) throws VizException {
|
||||
int idx = (int) (ijcoord.x + (ijcoord.y * this.gridDims[0]));
|
||||
|
||||
float spd = this.magnitude.get(idx);
|
||||
float dir = this.direction.get(idx) - 180;
|
||||
float dir = this.direction.get(idx);
|
||||
|
||||
if (dir < -999999 || dir > 9999999) {
|
||||
// perhaps this check should limit +/- 180
|
||||
|
@ -161,9 +177,13 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
newWorldLocation[1]);
|
||||
}
|
||||
|
||||
dir = dir
|
||||
+ (float) MapUtil.rotation(latLon,
|
||||
GridGeometry2D.wrap(gridGeometryOfGrid));
|
||||
if (gridRelative) {
|
||||
// rotate data from grid up to true north
|
||||
dir += (float) MapUtil.rotation(latLon,
|
||||
GridGeometry2D.wrap(gridGeometryOfGrid));
|
||||
}
|
||||
|
||||
// rotate dir from true north to display up
|
||||
dir -= this.gc.getAzimuth();
|
||||
} catch (Exception e) {
|
||||
throw new VizException(e);
|
||||
|
@ -185,14 +205,6 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setScale(double scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public void setLineWidth(int lineWidth) {
|
||||
this.lineWidth = lineWidth;
|
||||
}
|
||||
|
@ -208,6 +220,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
* @param density
|
||||
* the density to set
|
||||
*/
|
||||
@Override
|
||||
public boolean setDensity(double density) {
|
||||
if (super.setDensity(density)) {
|
||||
disposeResources();
|
||||
|
@ -223,6 +236,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
* @param magnification
|
||||
* the magnification to set
|
||||
*/
|
||||
@Override
|
||||
public boolean setMagnification(double magnification) {
|
||||
if (super.setMagnification(magnification)) {
|
||||
disposeResources();
|
||||
|
@ -245,6 +259,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
return direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disposeResources() {
|
||||
if (vectorRenderable != null) {
|
||||
vectorRenderable.dispose();
|
||||
|
@ -252,6 +267,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Coordinate createResource(Coordinate coord) throws VizException {
|
||||
return coord;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.viz.core.contours.util;
|
||||
|
||||
import com.raytheon.uf.viz.core.IGraphicsTarget;
|
||||
import com.raytheon.uf.viz.core.drawables.IDescriptor;
|
||||
|
||||
/**
|
||||
* Interface for factory class to create a VectorGraphicsRenderable for
|
||||
* GriddedVectorDisplay
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Aug 22, 2013 #2287 randerso Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author randerso
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public interface IVectorGraphicsRenderableFactory {
|
||||
public VectorGraphicsRenderable createRenderable(IDescriptor descriptor,
|
||||
IGraphicsTarget target, double size);
|
||||
}
|
|
@ -38,7 +38,8 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* May 27, 2011 bsteffen Initial creation
|
||||
* May 27, 2011 bsteffen Initial creation
|
||||
* Aug 27, 2013 #2287 randerso Refactored to allow subclassing
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -47,17 +48,17 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
*/
|
||||
public class VectorGraphicsRenderable {
|
||||
|
||||
private IWireframeShape lastShape;
|
||||
protected IWireframeShape lastShape;
|
||||
|
||||
private double size = 80;
|
||||
protected double size = 80;
|
||||
|
||||
private double scale = 1.0;
|
||||
protected double scale = 1.0;
|
||||
|
||||
private RGB color;
|
||||
protected RGB color;
|
||||
|
||||
private float lineWidth = 1.0f;
|
||||
protected float lineWidth = 1.0f;
|
||||
|
||||
private LineStyle lineStyle;
|
||||
protected LineStyle lineStyle;
|
||||
|
||||
public VectorGraphicsRenderable(IDescriptor descriptor,
|
||||
IGraphicsTarget target, double size, double scale) {
|
||||
|
@ -89,36 +90,7 @@ public class VectorGraphicsRenderable {
|
|||
public void paintBarb(Coordinate plotLoc, double adjSize, double spd,
|
||||
double dir) {
|
||||
if (spd < 2.5) {
|
||||
double[][] line = new double[9][2];
|
||||
|
||||
double aa = adjSize * .030;
|
||||
double saa = aa * 0.707;
|
||||
|
||||
line[8][0] = line[0][0] = plotLoc.x + aa;
|
||||
line[8][1] = line[0][1] = plotLoc.y;
|
||||
line[1][0] = plotLoc.x + saa;
|
||||
line[1][1] = plotLoc.y + saa;
|
||||
|
||||
line[2][0] = plotLoc.x;
|
||||
line[2][1] = plotLoc.y + aa;
|
||||
|
||||
line[3][0] = plotLoc.x - saa;
|
||||
line[3][1] = plotLoc.y + saa;
|
||||
|
||||
line[4][0] = plotLoc.x - aa;
|
||||
line[4][1] = plotLoc.y;
|
||||
|
||||
line[5][0] = plotLoc.x - saa;
|
||||
line[5][1] = plotLoc.y - saa;
|
||||
|
||||
line[6][0] = plotLoc.x;
|
||||
line[6][1] = plotLoc.y - aa;
|
||||
|
||||
line[7][0] = plotLoc.x + saa;
|
||||
line[7][1] = plotLoc.y - saa;
|
||||
|
||||
lastShape.addLineSegment(line);
|
||||
|
||||
paintPoint(plotLoc, adjSize);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -198,6 +170,38 @@ public class VectorGraphicsRenderable {
|
|||
}
|
||||
}
|
||||
|
||||
protected void paintPoint(Coordinate plotLoc, double adjSize) {
|
||||
double[][] line = new double[9][2];
|
||||
|
||||
double aa = adjSize * .030;
|
||||
double saa = aa * 0.707;
|
||||
|
||||
line[8][0] = line[0][0] = plotLoc.x + aa;
|
||||
line[8][1] = line[0][1] = plotLoc.y;
|
||||
line[1][0] = plotLoc.x + saa;
|
||||
line[1][1] = plotLoc.y + saa;
|
||||
|
||||
line[2][0] = plotLoc.x;
|
||||
line[2][1] = plotLoc.y + aa;
|
||||
|
||||
line[3][0] = plotLoc.x - saa;
|
||||
line[3][1] = plotLoc.y + saa;
|
||||
|
||||
line[4][0] = plotLoc.x - aa;
|
||||
line[4][1] = plotLoc.y;
|
||||
|
||||
line[5][0] = plotLoc.x - saa;
|
||||
line[5][1] = plotLoc.y - saa;
|
||||
|
||||
line[6][0] = plotLoc.x;
|
||||
line[6][1] = plotLoc.y - aa;
|
||||
|
||||
line[7][0] = plotLoc.x + saa;
|
||||
line[7][1] = plotLoc.y - saa;
|
||||
|
||||
lastShape.addLineSegment(line);
|
||||
}
|
||||
|
||||
public void paintDualArrow(Coordinate plotLoc, double adjSize, double spd,
|
||||
double dir) {
|
||||
if (spd < 4.0) {
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.viz.core.contours.util;
|
||||
|
||||
import com.raytheon.uf.viz.core.IGraphicsTarget;
|
||||
import com.raytheon.uf.viz.core.drawables.IDescriptor;
|
||||
|
||||
/**
|
||||
* VectorGraphicsRenderable Factory
|
||||
*
|
||||
* Constructs the VectorGraphicsRenderable for D2D usage of GriddedVectorDisplay
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Aug 22, 2013 #2287 randerso Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author randerso
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class VectorGraphicsRenderableFactory implements
|
||||
IVectorGraphicsRenderableFactory {
|
||||
private double scale;
|
||||
|
||||
public VectorGraphicsRenderableFactory() {
|
||||
this.scale = 0.6;
|
||||
}
|
||||
|
||||
public VectorGraphicsRenderableFactory(double scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scale
|
||||
* the scale to set
|
||||
*/
|
||||
public void setScale(double scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.viz.core.contours.util.IVectorGraphicsRenderableFactory#
|
||||
* createRenderable()
|
||||
*/
|
||||
@Override
|
||||
public VectorGraphicsRenderable createRenderable(IDescriptor descriptor,
|
||||
IGraphicsTarget target, double size) {
|
||||
return new VectorGraphicsRenderable(descriptor, target, size, scale);
|
||||
}
|
||||
|
||||
}
|
|
@ -161,6 +161,7 @@ import com.vividsolutions.jts.geom.Envelope;
|
|||
* May 11, 2012 njensen Allow rsc to be recycled
|
||||
* Nov 08, 2012 1298 rferrel Changes for non-blocking FuzzValueDialog.
|
||||
* Mar 04, 2013 1637 randerso Fix time matching for ISC grids
|
||||
* Aug 27, 2013 2287 randerso Fixed scaling and direction of wind arrows
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -617,6 +618,7 @@ public class GFEResource extends
|
|||
}
|
||||
|
||||
clearVectorDisplays();
|
||||
GFEVectorGraphicsRenderableFactory factory;
|
||||
for (VisualizationType type : visTypes) {
|
||||
switch (type) {
|
||||
case WIND_ARROW:
|
||||
|
@ -626,24 +628,31 @@ public class GFEResource extends
|
|||
if (logFactor < 0.0) {
|
||||
logFactor = 0.0;
|
||||
}
|
||||
// TODO: add logFactor to PointWindDisplay,
|
||||
// GriddedVectorDisplay
|
||||
factory = new GFEVectorGraphicsRenderableFactory(
|
||||
logFactor, parm.getGridInfo().getMaxValue());
|
||||
|
||||
this.vectorDisplay.add(new GriddedVectorDisplay(
|
||||
mag, dir, descriptor, MapUtil
|
||||
.getGridGeometry(gs.getGridInfo()
|
||||
.getGridLoc()),
|
||||
getVectorSize("WindArrowDefaultSize"),
|
||||
visTypeToDisplayType(type)));
|
||||
1.36, false, visTypeToDisplayType(type),
|
||||
factory));
|
||||
break;
|
||||
|
||||
case WIND_BARB:
|
||||
this.vectorDisplay.add(new GriddedVectorDisplay(
|
||||
mag, dir, descriptor, MapUtil
|
||||
.getGridGeometry(gs.getGridInfo()
|
||||
.getGridLoc()),
|
||||
getVectorSize("WindBarbDefaultSize"),
|
||||
visTypeToDisplayType(type)));
|
||||
factory = new GFEVectorGraphicsRenderableFactory(
|
||||
0.0, parm.getGridInfo().getMaxValue());
|
||||
this.vectorDisplay
|
||||
.add(new GriddedVectorDisplay(
|
||||
mag,
|
||||
dir,
|
||||
descriptor,
|
||||
MapUtil.getGridGeometry(gs
|
||||
.getGridInfo().getGridLoc()),
|
||||
getVectorSize("WindBarbDefaultSize"),
|
||||
1.36, false,
|
||||
visTypeToDisplayType(type), factory));
|
||||
break;
|
||||
|
||||
case IMAGE:
|
||||
|
@ -1000,8 +1009,6 @@ public class GFEResource extends
|
|||
size = 60;
|
||||
}
|
||||
|
||||
size = (int) (size / 0.8);
|
||||
|
||||
int offset = parm.getDisplayAttributes().getFontOffset()
|
||||
+ Activator.getDefault().getPreferenceStore()
|
||||
.getInt("Contour_font");
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.viz.gfe.rsc;
|
||||
|
||||
import com.raytheon.uf.viz.core.IGraphicsTarget;
|
||||
import com.raytheon.uf.viz.core.drawables.IDescriptor;
|
||||
import com.raytheon.viz.core.contours.util.VectorGraphicsRenderable;
|
||||
import com.vividsolutions.jts.geom.Coordinate;
|
||||
|
||||
/**
|
||||
* GFE version of VectorGraphicsRenderable. Subclassed to better match A1 GFE
|
||||
* behavior
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Aug 22, 2013 #2287 randerso Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author randerso
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class GFEVectorGraphicsRenderable extends VectorGraphicsRenderable {
|
||||
double minLog = 0.0;
|
||||
|
||||
double maxLog = 0.0;
|
||||
|
||||
private double maxLimit;
|
||||
|
||||
/**
|
||||
* @param descriptor
|
||||
* @param target
|
||||
* @param size
|
||||
* @param logFactor
|
||||
* @param maxLimit
|
||||
*/
|
||||
public GFEVectorGraphicsRenderable(IDescriptor descriptor,
|
||||
IGraphicsTarget target, double size, double logFactor,
|
||||
double maxLimit) {
|
||||
super(descriptor, target, size, logFactor);
|
||||
|
||||
this.maxLimit = maxLimit;
|
||||
if (logFactor > 0.0) {
|
||||
minLog = Math.log(logFactor);
|
||||
maxLog = Math.log(logFactor + 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param plotLoc
|
||||
* @param adjSize
|
||||
* @param spd
|
||||
* @param dir
|
||||
* barb direction in radians
|
||||
*/
|
||||
@Override
|
||||
public void paintBarb(Coordinate plotLoc, double adjSize, double spd,
|
||||
double dir) {
|
||||
paintPoint(plotLoc, adjSize);
|
||||
|
||||
int speed = (int) (spd + 2.5);
|
||||
double staff = adjSize * .4;
|
||||
double barb = staff * 0.30;
|
||||
double add = staff * 0.105;
|
||||
// DIRECTIONS
|
||||
double uudd = -spd * Math.sin(dir);
|
||||
double vvff = -spd * Math.cos(dir);
|
||||
double dix = -uudd / spd;
|
||||
double djy = -vvff / spd;
|
||||
double dix1 = Math.cos(Math.toRadians(75)) * dix
|
||||
+ Math.sin(Math.toRadians(75)) * djy;
|
||||
double djy1 = (-1) * Math.sin(Math.toRadians(75)) * dix
|
||||
+ Math.cos(Math.toRadians(75)) * djy;
|
||||
|
||||
// SPEED AND COUNTERS:
|
||||
int n50 = speed / 50;
|
||||
int calcSpd = speed - 50 * n50;
|
||||
int n10 = calcSpd / 10;
|
||||
calcSpd = calcSpd - 10 * n10;
|
||||
int n5 = calcSpd / 5;
|
||||
double sx = ((n50 + n50 + n10 + n5 + 2)) * add;
|
||||
staff = Math.max(adjSize * .4, sx);
|
||||
|
||||
// DRAW STAFF
|
||||
double ix2 = plotLoc.x;
|
||||
double jy2 = plotLoc.y;
|
||||
double ix1 = ix2 + dix * staff;
|
||||
double jy1 = jy2 - djy * staff;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 }, { ix1, jy1 } });
|
||||
|
||||
// PLOT LONE HALF-BARB, IF NECESSARY
|
||||
if (n50 == 0 && n10 == 0) {
|
||||
ix2 = ix1 - dix * add;
|
||||
jy2 = jy1 + djy * add;
|
||||
ix1 = ix2 + dix1 * barb / 2.0;
|
||||
jy1 = jy2 - djy1 * barb / 2.0;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
return;
|
||||
}
|
||||
|
||||
// PLOT FLAGS, IF NECESSARY
|
||||
for (int i = 0; i < n50; i++) {
|
||||
ix2 = ix1 + dix1 * barb;
|
||||
jy2 = jy1 - djy1 * barb;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
ix1 = ix1 - dix * add * 2;
|
||||
jy1 = jy1 + djy * add * 2;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
}
|
||||
if (n50 > 0) {
|
||||
ix1 = ix1 - dix * add / 2.0;
|
||||
jy1 = jy1 + djy * add / 2.0;
|
||||
}
|
||||
|
||||
// PLOT BARB, IF NECESSARY
|
||||
for (int i = 0; i < n10; i++) {
|
||||
ix2 = ix1 + dix1 * barb;
|
||||
jy2 = jy1 - djy1 * barb;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
ix1 = ix1 - dix * add;
|
||||
jy1 = jy1 + djy * add;
|
||||
}
|
||||
|
||||
// PLOT HALF-BARB, IF NECESSARY
|
||||
if (n5 != 0) {
|
||||
ix2 = ix1 + dix1 * barb / 2.0;
|
||||
jy2 = jy1 - djy1 * barb / 2.0;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintArrow(Coordinate plotLoc, double adjSize, double mag,
|
||||
double dir) {
|
||||
paintPoint(plotLoc, adjSize);
|
||||
|
||||
double staff = 0.0;
|
||||
|
||||
double logFactor = this.scale;
|
||||
|
||||
// linear scaling
|
||||
if (logFactor == 0.00) {
|
||||
staff = mag * size / maxLimit;
|
||||
} else {
|
||||
double pcentRange = mag / maxLimit;
|
||||
double lg = Math.log(logFactor + pcentRange);
|
||||
double pcentLog = (lg - minLog) / (maxLog - minLog);
|
||||
staff = pcentLog * size;
|
||||
}
|
||||
|
||||
double barb = staff / 7.0;
|
||||
|
||||
// if (staff < barb) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
double ratio = adjSize / size;
|
||||
staff *= ratio;
|
||||
barb *= ratio;
|
||||
|
||||
// DIRECTIONS
|
||||
double uudd = -mag * Math.sin(dir);
|
||||
double vvff = -mag * Math.cos(dir);
|
||||
double dix = uudd / mag;
|
||||
double djy = vvff / mag;
|
||||
double dix1 = -dix - djy;
|
||||
double djy1 = dix - djy;
|
||||
double dix2 = -dix + djy;
|
||||
double djy2 = -dix - djy;
|
||||
|
||||
// DRAW BODY OF ARROW
|
||||
double ix2 = plotLoc.x;
|
||||
double jy2 = plotLoc.y;
|
||||
double ix1 = ix2 + dix * staff;
|
||||
double jy1 = jy2 - djy * staff;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 }, { ix1, jy1 } });
|
||||
// DRAW HEAD OF ARROW.
|
||||
ix2 = ix1 + dix1 * barb;
|
||||
jy2 = jy1 - djy1 * barb;
|
||||
double ix3 = ix1 + dix2 * barb;
|
||||
double jy3 = jy1 - djy2 * barb;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 }, { ix1, jy1 },
|
||||
{ ix3, jy3 } });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.viz.gfe.rsc;
|
||||
|
||||
import com.raytheon.uf.viz.core.IGraphicsTarget;
|
||||
import com.raytheon.uf.viz.core.drawables.IDescriptor;
|
||||
import com.raytheon.viz.core.contours.util.IVectorGraphicsRenderableFactory;
|
||||
import com.raytheon.viz.core.contours.util.VectorGraphicsRenderable;
|
||||
|
||||
/**
|
||||
* GFE VectorGraphicsRenderable Factory
|
||||
*
|
||||
* Constructs the VectorGraphicsRenderable for GFE usage of GriddedVectorDisplay
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Aug 22, 2013 #2287 randerso Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author randerso
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class GFEVectorGraphicsRenderableFactory implements
|
||||
IVectorGraphicsRenderableFactory {
|
||||
private double logFactor;
|
||||
|
||||
private double maxLimit;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param logFactor
|
||||
* logFactor scaling value from parm_arrowScaling preference
|
||||
* @param maxLimit
|
||||
* max allowable value for parm
|
||||
*/
|
||||
public GFEVectorGraphicsRenderableFactory(double logFactor, double maxLimit) {
|
||||
this.logFactor = logFactor;
|
||||
this.maxLimit = maxLimit;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* com.raytheon.viz.core.contours.util.IVectorGraphicsRenderableFactory#
|
||||
* createRenderable(com.raytheon.uf.viz.core.drawables.IDescriptor,
|
||||
* com.raytheon.uf.viz.core.IGraphicsTarget, double)
|
||||
*/
|
||||
@Override
|
||||
public VectorGraphicsRenderable createRenderable(IDescriptor descriptor,
|
||||
IGraphicsTarget target, double size) {
|
||||
return new GFEVectorGraphicsRenderable(descriptor, target, size,
|
||||
logFactor, maxLimit);
|
||||
}
|
||||
|
||||
}
|
|
@ -50,6 +50,8 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 23, 2010 bsteffen Initial creation
|
||||
* Aug 27, 2013 #2287 randerso Added densityFactor to allow application specific
|
||||
* adjustment of density.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -68,13 +70,17 @@ public class GriddedIconDisplay extends AbstractGriddedDisplay<IImage> {
|
|||
private PointIconFactory iconFactory;
|
||||
|
||||
/**
|
||||
* @param values
|
||||
* @param descriptor
|
||||
* @param gridGeometryOfGrid
|
||||
* @param imageSize
|
||||
* @param densityFactor
|
||||
* adjustment factor to make density match A1
|
||||
*/
|
||||
public GriddedIconDisplay(float[] values, IMapDescriptor descriptor,
|
||||
GeneralGridGeometry gridGeometryOfGrid, int imageSize) {
|
||||
super(descriptor, gridGeometryOfGrid, imageSize);
|
||||
GeneralGridGeometry gridGeometryOfGrid, int imageSize,
|
||||
double densityFactor) {
|
||||
super(descriptor, gridGeometryOfGrid, imageSize, densityFactor);
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
|
@ -126,6 +132,7 @@ public class GriddedIconDisplay extends AbstractGriddedDisplay<IImage> {
|
|||
return (int) values[idx];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setColor(RGB color) {
|
||||
if (super.setColor(color)) {
|
||||
iconFactory = null;
|
||||
|
@ -166,8 +173,8 @@ public class GriddedIconDisplay extends AbstractGriddedDisplay<IImage> {
|
|||
if (renderable.resource != empty) {
|
||||
PointImage image = new PointImage(renderable.resource,
|
||||
renderable.plotLocation);
|
||||
image.setHeight((double) size * magnification);
|
||||
image.setWidth((double) size * magnification);
|
||||
image.setHeight(size * magnification);
|
||||
image.setWidth(size * magnification);
|
||||
images.add(image);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ import com.raytheon.viz.core.contours.rsc.displays.AbstractGriddedDisplay;
|
|||
import com.raytheon.viz.core.contours.rsc.displays.GriddedContourDisplay;
|
||||
import com.raytheon.viz.core.contours.rsc.displays.GriddedStreamlineDisplay;
|
||||
import com.raytheon.viz.core.contours.rsc.displays.GriddedVectorDisplay;
|
||||
import com.raytheon.viz.core.contours.util.VectorGraphicsRenderableFactory;
|
||||
import com.raytheon.viz.core.drawables.ColorMapParameterFactory;
|
||||
import com.raytheon.viz.core.rsc.displays.GriddedImageDisplay2;
|
||||
import com.raytheon.viz.core.style.arrow.ArrowPreferences;
|
||||
|
@ -111,6 +112,8 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
* May 08, 2013 1980 bsteffen Set paint status in GridResources for
|
||||
* KML.
|
||||
* Jul 15, 2013 2107 bsteffen Fix sampling of grid vector arrows.
|
||||
* Aug 27, 2013 2287 randerso Added new parameters required by GriddedVectorDisplay
|
||||
* and GriddedIconDisplay
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -154,13 +157,14 @@ public abstract class AbstractGridResource<T extends AbstractResourceData>
|
|||
protected AbstractGridResource(T resourceData, LoadProperties loadProperties) {
|
||||
super(resourceData, loadProperties);
|
||||
resourceData.addChangeListener(new IResourceDataChanged() {
|
||||
@Override
|
||||
public void resourceChanged(ChangeType type, Object object) {
|
||||
if (type == ChangeType.DATA_UPDATE) {
|
||||
if (object instanceof PluginDataObject) {
|
||||
addDataObject((PluginDataObject) object);
|
||||
} else if (object instanceof PluginDataObject[]) {
|
||||
for (PluginDataObject pdo : (PluginDataObject[]) object) {
|
||||
addDataObject((PluginDataObject) pdo);
|
||||
addDataObject(pdo);
|
||||
}
|
||||
} else if (object instanceof Object[]) {
|
||||
for (Object obj : (Object[]) object) {
|
||||
|
@ -489,9 +493,10 @@ public abstract class AbstractGridResource<T extends AbstractResourceData>
|
|||
case ARROW:
|
||||
case DUALARROW:
|
||||
convertData(data);
|
||||
VectorGraphicsRenderableFactory factory = new VectorGraphicsRenderableFactory();
|
||||
GriddedVectorDisplay vectorDisplay = new GriddedVectorDisplay(
|
||||
data.getMagnitude(), data.getDirection(), descriptor,
|
||||
gridGeometry, 64, displayType);
|
||||
gridGeometry, 64, 0.75, true, displayType, factory);
|
||||
vectorDisplay.setColor(getCapability(ColorableCapability.class)
|
||||
.getColor());
|
||||
vectorDisplay.setLineStyle(getCapability(OutlineCapability.class)
|
||||
|
@ -504,14 +509,15 @@ public abstract class AbstractGridResource<T extends AbstractResourceData>
|
|||
MagnificationCapability.class).getMagnification());
|
||||
if (stylePreferences != null
|
||||
&& stylePreferences instanceof ArrowPreferences) {
|
||||
vectorDisplay.setScale(((ArrowPreferences) stylePreferences)
|
||||
factory.setScale(((ArrowPreferences) stylePreferences)
|
||||
.getScale());
|
||||
}
|
||||
renderable = vectorDisplay;
|
||||
break;
|
||||
case ICON:
|
||||
GriddedIconDisplay iconDisplay = new GriddedIconDisplay(data
|
||||
.getScalarData().array(), descriptor, gridGeometry, 80);
|
||||
.getScalarData().array(), descriptor, gridGeometry, 80,
|
||||
0.75);
|
||||
iconDisplay.setColor(getCapability(ColorableCapability.class)
|
||||
.getColor());
|
||||
iconDisplay.setDensity(getCapability(DensityCapability.class)
|
||||
|
|
|
@ -79,6 +79,8 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
* constructor to avoid duplicate data
|
||||
* requests.
|
||||
* Jul 15, 2013 2107 bsteffen Fix sampling of grid vector arrows.
|
||||
* Aug 27, 2013 2287 randerso Removed 180 degree adjustment required by error
|
||||
* in Maputil.rotation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -174,7 +176,7 @@ public class D2DGridResource extends GridResource<GridResourceData> implements
|
|||
crs2ll.transform(dp, dp);
|
||||
Coordinate ll = new Coordinate(dp.x, dp.y);
|
||||
float rot = (float) MapUtil.rotation(ll, geom);
|
||||
dir = (dir + rot + 180) % 360;
|
||||
dir = (dir + rot) % 360;
|
||||
data.getScalarData().put(index, dir);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,9 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Mar 9, 2011 bsteffen Initial creation
|
||||
* Mar 9, 2011 bsteffen Initial creation
|
||||
* Aug 27, 2013 #2287 randerso Removed 180 degree adjustment required by error
|
||||
* in Maputil.rotation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -247,8 +249,8 @@ public class GeneralGridData {
|
|||
Coordinate ll = new Coordinate(dp.x, dp.y);
|
||||
double rot = MapUtil.rotation(ll, newGeom);
|
||||
double rot2 = MapUtil.rotation(ll, gridGeometry);
|
||||
double cos = Math.cos(Math.toRadians(180 + rot - rot2));
|
||||
double sin = Math.sin(Math.toRadians(180 + rot - rot2));
|
||||
double cos = Math.cos(Math.toRadians(rot - rot2));
|
||||
double sin = Math.sin(Math.toRadians(rot - rot2));
|
||||
double u = udata[index];
|
||||
double v = vdata[index];
|
||||
udata[index] = (float) (cos * u - sin * v);
|
||||
|
|
|
@ -63,6 +63,8 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
* 5/16/08 875 bphillip Initial Creation.
|
||||
* 10/10/12 #1260 randerso Added getters for source and destination glocs
|
||||
* 02/19/13 #1637 randerso Fixed remapping of byte grids
|
||||
* 08/27/13 #2287 randerso Removed 180 degree adjustment required by error
|
||||
* in Maputil.rotation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -598,7 +600,7 @@ public class RemapGrid {
|
|||
Coordinate llc = destinationGloc
|
||||
.latLonCenter(new Coordinate(x1, y1));
|
||||
this.rotation.set(x1, y1,
|
||||
(float) (180 - MapUtil.rotation(llc, sourceGloc)));
|
||||
(float) (-MapUtil.rotation(llc, sourceGloc)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,8 @@ import com.vividsolutions.jts.geom.Polygon;
|
|||
* 06/19/2012 14988 D. Friedman Make oversampling more like AWIPS 1
|
||||
* 09/18/2012 #1091 randerso corrected getBoundingEnvelope
|
||||
* 11/06/2012 15406 ryu Added convertToNativeEnvelope()
|
||||
* 08/27/2013 #2287 randerso Fixed rotation methods so it is not necessary
|
||||
* to subtract 180 from the returned value
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -1062,12 +1064,13 @@ public class MapUtil {
|
|||
* the right of UP is north (or 360) degrees.
|
||||
*
|
||||
* @param latLon
|
||||
* @param spatialObject
|
||||
* @return rotation angle
|
||||
*/
|
||||
public static double rotation(Coordinate latLon,
|
||||
ISpatialObject spatialObject) {
|
||||
|
||||
double newLatLonY = latLon.y + 0.05;
|
||||
double newLatLonY = latLon.y - 0.05;
|
||||
if (newLatLonY > 90) {
|
||||
newLatLonY -= 180;
|
||||
}
|
||||
|
@ -1098,11 +1101,12 @@ public class MapUtil {
|
|||
* the right of UP is north (or 360) degrees.
|
||||
*
|
||||
* @param latLon
|
||||
* @param geometry
|
||||
* @return rotation angle
|
||||
*/
|
||||
public static double rotation(Coordinate latLon, GridGeometry2D geometry) {
|
||||
|
||||
double newLatLonY = latLon.y + 0.05;
|
||||
double newLatLonY = latLon.y - 0.05;
|
||||
if (newLatLonY > 90) {
|
||||
newLatLonY -= 180;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package gov.noaa.nws.ncep.viz.rsc.ncgrid.contours;
|
||||
|
||||
|
||||
import gov.noaa.nws.ncep.common.log.logger.NcepLogger;
|
||||
import gov.noaa.nws.ncep.common.log.logger.NcepLoggerManager;
|
||||
import gov.noaa.nws.ncep.edex.common.dataRecords.NcFloatDataRecord;
|
||||
|
@ -56,7 +55,9 @@ import com.vividsolutions.jts.geom.Coordinate;
|
|||
*/
|
||||
public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
||||
|
||||
private static NcepLogger logger = NcepLoggerManager.getNcepLogger(GriddedVectorDisplay.class);
|
||||
private static NcepLogger logger = NcepLoggerManager
|
||||
.getNcepLogger(GriddedVectorDisplay.class);
|
||||
|
||||
private final FloatBuffer magnitude;
|
||||
|
||||
private final FloatBuffer direction;
|
||||
|
@ -64,7 +65,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
private final ISpatialObject gridLocation;
|
||||
|
||||
private final int SIZE = 64;
|
||||
|
||||
|
||||
private float lineWidth = 1;
|
||||
|
||||
private LineStyle lineStyle = LineStyle.SOLID;
|
||||
|
@ -80,93 +81,97 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
private GeodeticCalculator gc;
|
||||
|
||||
private NcFloatDataRecord data;
|
||||
|
||||
|
||||
private ContourAttributes contourAttributes;
|
||||
|
||||
|
||||
private Coordinate latLon;
|
||||
|
||||
|
||||
private boolean isDirectional;
|
||||
|
||||
|
||||
private static NcgribLogger ncgribLogger;
|
||||
|
||||
public GriddedVectorDisplay(NcFloatDataRecord rec,
|
||||
DisplayType displayType, boolean directional, IMapDescriptor descriptor,
|
||||
|
||||
public GriddedVectorDisplay(NcFloatDataRecord rec, DisplayType displayType,
|
||||
boolean directional, IMapDescriptor descriptor,
|
||||
ISpatialObject gridLocation, ContourAttributes attrs) {
|
||||
super(descriptor, MapUtil.getGridGeometry(gridLocation),gridLocation.getNx(),gridLocation.getNy());
|
||||
super(descriptor, MapUtil.getGridGeometry(gridLocation), gridLocation
|
||||
.getNx(), gridLocation.getNy());
|
||||
long t1 = System.currentTimeMillis();
|
||||
this.data = rec;
|
||||
this.contourAttributes = attrs;
|
||||
ncgribLogger = NcgribLogger.getInstance();
|
||||
|
||||
|
||||
if (directional) {
|
||||
float[] dir = rec.getXdata();
|
||||
float[] spd = new float[dir.length];
|
||||
for (int i = 0; i < dir.length; i++) {
|
||||
if (dir[i] == -999999.0f) {
|
||||
dir[i] = -999999.0f;
|
||||
spd[i] = -999999.0f;
|
||||
} else {
|
||||
spd[i] = 40;
|
||||
}
|
||||
}
|
||||
this.magnitude = FloatBuffer.wrap(spd);
|
||||
this.direction = FloatBuffer.wrap(dir);
|
||||
}
|
||||
else {
|
||||
float[] dirX = rec.getXdata();
|
||||
float[] dirY = rec.getYdata();
|
||||
|
||||
float [] spd = new float[dirX.length];
|
||||
float [] dir = new float[dirX.length];
|
||||
|
||||
|
||||
for (int i = 0; i < spd.length; i++) {
|
||||
if (dirX[i] == -999999.0f || dirY[i] == -999999.0f) {
|
||||
spd[i] = -999999.0f;
|
||||
dir[i] = -999999.0f;
|
||||
}
|
||||
else {
|
||||
spd[i] = (float) Math.hypot(dirX[i], dirY[i]);
|
||||
dir[i] = (float) (Math.atan2(dirX[i], dirY[i]) * 180 / Math.PI) + 180;
|
||||
}
|
||||
}
|
||||
|
||||
this.magnitude = FloatBuffer.wrap(spd);
|
||||
this.direction = FloatBuffer.wrap(dir);
|
||||
float[] dir = rec.getXdata();
|
||||
float[] spd = new float[dir.length];
|
||||
for (int i = 0; i < dir.length; i++) {
|
||||
if (dir[i] == -999999.0f) {
|
||||
dir[i] = -999999.0f;
|
||||
spd[i] = -999999.0f;
|
||||
} else {
|
||||
spd[i] = 40;
|
||||
}
|
||||
}
|
||||
this.magnitude = FloatBuffer.wrap(spd);
|
||||
this.direction = FloatBuffer.wrap(dir);
|
||||
} else {
|
||||
float[] dirX = rec.getXdata();
|
||||
float[] dirY = rec.getYdata();
|
||||
|
||||
float[] spd = new float[dirX.length];
|
||||
float[] dir = new float[dirX.length];
|
||||
|
||||
for (int i = 0; i < spd.length; i++) {
|
||||
if (dirX[i] == -999999.0f || dirY[i] == -999999.0f) {
|
||||
spd[i] = -999999.0f;
|
||||
dir[i] = -999999.0f;
|
||||
} else {
|
||||
spd[i] = (float) Math.hypot(dirX[i], dirY[i]);
|
||||
dir[i] = (float) (Math.atan2(dirX[i], dirY[i]) * 180 / Math.PI) + 180;
|
||||
}
|
||||
}
|
||||
|
||||
this.magnitude = FloatBuffer.wrap(spd);
|
||||
this.direction = FloatBuffer.wrap(dir);
|
||||
}
|
||||
long t2 = System.currentTimeMillis();
|
||||
logger.debug("GriddedVectorDisplay after check -999999 took:" + (t2-t1));
|
||||
logger.debug("GriddedVectorDisplay after check -999999 took:"
|
||||
+ (t2 - t1));
|
||||
this.gridLocation = gridLocation;
|
||||
this.displayType = displayType;
|
||||
this.gc = new GeodeticCalculator(descriptor.getCRS());
|
||||
this.isDirectional = directional;
|
||||
|
||||
|
||||
int colorIndex = 31;
|
||||
float sizeFactor = 1;
|
||||
lineWidth = 1;
|
||||
String windAttr = attrs.getWind();
|
||||
if (windAttr != null && !windAttr.isEmpty()) {
|
||||
if (windAttr.contains("bk")) windAttr = windAttr.replace("bk", "");
|
||||
String [] attr = windAttr.trim().split("/");
|
||||
colorIndex = Integer.parseInt(attr[0].trim());
|
||||
if (attr.length >= 2 && !attr[1].trim().isEmpty()) sizeFactor = Float.parseFloat(attr[1].trim());
|
||||
if (attr.length >= 3 && !attr[2].trim().isEmpty()) lineWidth = Float.parseFloat(attr[2].trim());
|
||||
|
||||
if (windAttr.contains("bk")) {
|
||||
windAttr = windAttr.replace("bk", "");
|
||||
}
|
||||
String[] attr = windAttr.trim().split("/");
|
||||
colorIndex = Integer.parseInt(attr[0].trim());
|
||||
if (attr.length >= 2 && !attr[1].trim().isEmpty()) {
|
||||
sizeFactor = Float.parseFloat(attr[1].trim());
|
||||
}
|
||||
if (attr.length >= 3 && !attr[2].trim().isEmpty()) {
|
||||
lineWidth = Float.parseFloat(attr[2].trim());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
setSize(sizeFactor * SIZE);
|
||||
setColor(GempakColor.convertToRGB(colorIndex));
|
||||
setColor(GempakColor.convertToRGB(colorIndex));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void paint(NcgridResourceData gridRscData, IGraphicsTarget target, PaintProperties paintProps)
|
||||
throws VizException {
|
||||
|
||||
if (paintProps.isZooming()) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void paint(NcgridResourceData gridRscData, IGraphicsTarget target,
|
||||
PaintProperties paintProps) throws VizException {
|
||||
|
||||
if (paintProps.isZooming()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastExtent == null
|
||||
|| !lastExtent.equals(paintProps.getView().getExtent())) {
|
||||
disposeImages();
|
||||
|
@ -177,55 +182,64 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
super.paint(gridRscData, target, paintProps);
|
||||
lastShape.compile();
|
||||
}
|
||||
|
||||
|
||||
target.drawWireframeShape(lastShape, color, lineWidth, lineStyle);
|
||||
}
|
||||
|
||||
public void createWireFrame (NcgridResourceData gridRscData, IGraphicsTarget target, PaintProperties paintProps) {
|
||||
public void createWireFrame(NcgridResourceData gridRscData,
|
||||
IGraphicsTarget target, PaintProperties paintProps) {
|
||||
|
||||
if (lastShape == null) {
|
||||
try {
|
||||
long t1 = System.currentTimeMillis();
|
||||
lastShape = target.createWireframeShape(false, descriptor);
|
||||
super.paint(gridRscData, target, paintProps);
|
||||
lastShape.compile();
|
||||
long t4 = System.currentTimeMillis();
|
||||
if ( ncgribLogger.enableCntrLogs() )
|
||||
logger.info("--GriddedVectorDisplay: create wireframe took:" + (t4-t1));
|
||||
}
|
||||
catch ( VizException e) {
|
||||
lastShape = null;
|
||||
}
|
||||
}
|
||||
if (lastShape == null) {
|
||||
try {
|
||||
long t1 = System.currentTimeMillis();
|
||||
lastShape = target.createWireframeShape(false, descriptor);
|
||||
super.paint(gridRscData, target, paintProps);
|
||||
lastShape.compile();
|
||||
long t4 = System.currentTimeMillis();
|
||||
if (ncgribLogger.enableCntrLogs()) {
|
||||
logger.info("--GriddedVectorDisplay: create wireframe took:"
|
||||
+ (t4 - t1));
|
||||
}
|
||||
} catch (VizException e) {
|
||||
lastShape = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void paintImage(int x, int y, PaintProperties paintProps, double adjSize) throws VizException {
|
||||
|
||||
@Override
|
||||
protected void paintImage(int x, int y, PaintProperties paintProps,
|
||||
double adjSize) throws VizException {
|
||||
int idx = x + y * this.gridDims[0];
|
||||
|
||||
// System.out.println("paintImage idx==="+idx+" x=="+ijcoord.x+" y====="+ijcoord.y);
|
||||
|
||||
if (idx < 0 || idx >= (gridDims[0] * gridDims[1])) return;
|
||||
// System.out.println("paintImage idx==="+idx+" x=="+ijcoord.x+" y====="+ijcoord.y);
|
||||
|
||||
if (idx < 0 || idx >= (gridDims[0] * gridDims[1])) {
|
||||
return;
|
||||
}
|
||||
float spd = this.magnitude.get(idx);
|
||||
float dir = this.direction.get(idx) - 180;
|
||||
float dir = this.direction.get(idx);
|
||||
|
||||
if (Float.isNaN(spd) || Float.isNaN(dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isPlotted[idx]) return;
|
||||
|
||||
ReferencedCoordinate newrco = new ReferencedCoordinate(
|
||||
new Coordinate(x, y),
|
||||
this.gridGeometryOfGrid, Type.GRID_CENTER);
|
||||
|
||||
if (this.isPlotted[idx]) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReferencedCoordinate newrco = new ReferencedCoordinate(new Coordinate(
|
||||
x, y), this.gridGeometryOfGrid, Type.GRID_CENTER);
|
||||
Coordinate plotLoc = null;
|
||||
|
||||
try {
|
||||
plotLoc = newrco.asPixel(this.descriptor
|
||||
.getGridGeometry());
|
||||
plotLoc = newrco.asPixel(this.descriptor.getGridGeometry());
|
||||
latLon = newrco.asLatLon();
|
||||
|
||||
if (latLon.x > 180 || latLon.x < -180 || latLon.y < -90 || latLon.y > 90) return;
|
||||
|
||||
|
||||
if (latLon.x > 180 || latLon.x < -180 || latLon.y < -90
|
||||
|| latLon.y > 90) {
|
||||
return;
|
||||
}
|
||||
|
||||
double[] stationLocation = { latLon.x, latLon.y };
|
||||
double[] stationPixelLocation = this.descriptor
|
||||
.worldToPixel(stationLocation);
|
||||
|
@ -260,15 +274,17 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
default:
|
||||
throw new VizException("Unsupported disply type: " + displayType);
|
||||
}
|
||||
|
||||
|
||||
this.isPlotted[idx] = true;
|
||||
}
|
||||
|
||||
private void paintBarb(Coordinate plotLoc, double adjSize, double spd,
|
||||
double dir) {
|
||||
//Don't plot missing value
|
||||
if ( spd < 0 ) return;
|
||||
|
||||
// Don't plot missing value
|
||||
if (spd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (spd < 2.5) {
|
||||
double[][] line = new double[9][2];
|
||||
|
||||
|
@ -335,32 +351,30 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
|
||||
// PLOT LONE HALF-BARB, IF NECESSARY
|
||||
if (n50 == 0 && n10 == 0) {
|
||||
if (latLon.y >= 0) {
|
||||
ix2 = ix1 + dix1 * barb / 2.0; //-
|
||||
jy2 = jy1 - djy1 * barb / 2.0; //+
|
||||
}
|
||||
else {
|
||||
ix2 = ix1 - dix1 * barb / 2.0;
|
||||
jy2 = jy1 + djy1 * barb / 2.0;
|
||||
}
|
||||
|
||||
ix1 = ix1 - dix * add;
|
||||
if (latLon.y >= 0) {
|
||||
ix2 = ix1 + dix1 * barb / 2.0; // -
|
||||
jy2 = jy1 - djy1 * barb / 2.0; // +
|
||||
} else {
|
||||
ix2 = ix1 - dix1 * barb / 2.0;
|
||||
jy2 = jy1 + djy1 * barb / 2.0;
|
||||
}
|
||||
|
||||
ix1 = ix1 - dix * add;
|
||||
jy1 = jy1 + djy * add;
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
return;
|
||||
}
|
||||
|
||||
// PLOT FLAGS, IF NECESSARY
|
||||
for (int i = 0; i < n50; i++) {
|
||||
if (latLon.y >= 0) {
|
||||
ix2 = ix1 + dix1 * barb; //+
|
||||
jy2 = jy1 - djy1 * barb; //-
|
||||
}
|
||||
else {
|
||||
ix2 = ix1 - dix1 * barb;
|
||||
jy2 = jy1 + djy1 * barb;
|
||||
}
|
||||
if (latLon.y >= 0) {
|
||||
ix2 = ix1 + dix1 * barb; // +
|
||||
jy2 = jy1 - djy1 * barb; // -
|
||||
} else {
|
||||
ix2 = ix1 - dix1 * barb;
|
||||
jy2 = jy1 + djy1 * barb;
|
||||
}
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
ix1 = ix1 - dix * add * 2; // 2 space
|
||||
|
@ -368,7 +382,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
}
|
||||
|
||||
|
||||
if (n50 > 0) {
|
||||
ix1 = ix1 - dix * add / 2.0;
|
||||
jy1 = jy1 + djy * add / 2.0;
|
||||
|
@ -376,14 +390,13 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
|
||||
// PLOT BARB, IF NECESSARY
|
||||
for (int i = 0; i < n10; i++) {
|
||||
if (latLon.y >= 0) {
|
||||
ix2 = ix1 + dix1 * barb; //+
|
||||
jy2 = jy1 - djy1 * barb;//-
|
||||
}
|
||||
else {
|
||||
ix2 = ix1 - dix1 * barb;
|
||||
jy2 = jy1 + djy1 * barb;
|
||||
}
|
||||
if (latLon.y >= 0) {
|
||||
ix2 = ix1 + dix1 * barb; // +
|
||||
jy2 = jy1 - djy1 * barb;// -
|
||||
} else {
|
||||
ix2 = ix1 - dix1 * barb;
|
||||
jy2 = jy1 + djy1 * barb;
|
||||
}
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
ix1 = ix1 - dix * add;
|
||||
|
@ -392,14 +405,13 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
|
||||
// PLOT HALF-BARB, IF NECESSARY
|
||||
if (n5 != 0) {
|
||||
if (latLon.y >= 0) {
|
||||
ix2 = ix1 + dix1 * barb / 2.0; //+
|
||||
jy2 = jy1 - djy1 * barb / 2.0; //-
|
||||
}
|
||||
else {
|
||||
ix2 = ix1 - dix1 * barb / 2.0;
|
||||
jy2 = jy1 + djy1 * barb / 2.0;
|
||||
}
|
||||
if (latLon.y >= 0) {
|
||||
ix2 = ix1 + dix1 * barb / 2.0; // +
|
||||
jy2 = jy1 - djy1 * barb / 2.0; // -
|
||||
} else {
|
||||
ix2 = ix1 - dix1 * barb / 2.0;
|
||||
jy2 = jy1 + djy1 * barb / 2.0;
|
||||
}
|
||||
lastShape.addLineSegment(new double[][] { { ix2, jy2 },
|
||||
{ ix1, jy1 } });
|
||||
}
|
||||
|
@ -519,8 +531,9 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
|
||||
/**
|
||||
* @param filter
|
||||
* the filter to set. Changed from density.
|
||||
* the filter to set. Changed from density.
|
||||
*/
|
||||
@Override
|
||||
public boolean setFilter(double filter) {
|
||||
if (super.setFilter(filter)) {
|
||||
disposeImages();
|
||||
|
@ -536,6 +549,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
* @param magnification
|
||||
* the magnification to set
|
||||
*/
|
||||
@Override
|
||||
public boolean setMagnification(double magnification) {
|
||||
if (super.setMagnification(magnification)) {
|
||||
disposeImages();
|
||||
|
@ -554,6 +568,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
return magnitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disposeImages() {
|
||||
if (lastShape != null) {
|
||||
lastShape.dispose();
|
||||
|
@ -561,6 +576,7 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Coordinate createImage(Coordinate coord) throws VizException {
|
||||
return coord;
|
||||
}
|
||||
|
@ -577,29 +593,34 @@ public class GriddedVectorDisplay extends AbstractGriddedDisplay<Coordinate> {
|
|||
return coord;
|
||||
}
|
||||
|
||||
public NcFloatDataRecord getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public boolean checkAttrsChanged (DisplayType type, boolean dir, String attr) {
|
||||
boolean isChanged = false;
|
||||
if ( this.displayType != type ||
|
||||
this.isDirectional != dir ||
|
||||
! this.contourAttributes.getWind().equalsIgnoreCase(attr)) {
|
||||
isChanged = true;
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public boolean isMatch ( ContourAttributes attr) {
|
||||
boolean match = false;
|
||||
if ( this.contourAttributes == null ) return match;
|
||||
if ( this.contourAttributes.getGlevel().equalsIgnoreCase(attr.getGlevel())&&
|
||||
this.contourAttributes.getGvcord().equalsIgnoreCase(attr.getGvcord()) &&
|
||||
this.contourAttributes.getScale().equalsIgnoreCase(attr.getScale()) &&
|
||||
this.contourAttributes.getGdpfun().equalsIgnoreCase(attr.getGdpfun()) ) {
|
||||
match = true;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
public NcFloatDataRecord getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public boolean checkAttrsChanged(DisplayType type, boolean dir, String attr) {
|
||||
boolean isChanged = false;
|
||||
if (this.displayType != type || this.isDirectional != dir
|
||||
|| !this.contourAttributes.getWind().equalsIgnoreCase(attr)) {
|
||||
isChanged = true;
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
public boolean isMatch(ContourAttributes attr) {
|
||||
boolean match = false;
|
||||
if (this.contourAttributes == null) {
|
||||
return match;
|
||||
}
|
||||
if (this.contourAttributes.getGlevel().equalsIgnoreCase(
|
||||
attr.getGlevel())
|
||||
&& this.contourAttributes.getGvcord().equalsIgnoreCase(
|
||||
attr.getGvcord())
|
||||
&& this.contourAttributes.getScale().equalsIgnoreCase(
|
||||
attr.getScale())
|
||||
&& this.contourAttributes.getGdpfun().equalsIgnoreCase(
|
||||
attr.getGdpfun())) {
|
||||
match = true;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue