package edu.colostate.vchill.plot; import edu.colostate.vchill.ViewUtil; import edu.colostate.vchill.chill.ChillNewExtTrackInfo; import edu.colostate.vchill.chill.ChillOldExtTrackInfo; import edu.colostate.vchill.chill.ChillTrackInfo; import edu.colostate.vchill.data.Ray; import java.awt.*; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * This class will be used to do all of the RHI plotting * That is required by the window. It also draws RHI grids * and handles information like mouse clicks to information. * * @author Justin Carlson * @author Jochen Deyke * @author jpont * @version 2010-08-02 * @created June 2, 2003 */ class ViewPlotMethodRHI extends ViewPlotMethod { private static final int startOffset = 16; public ViewPlotMethodRHI(final String type) { super(type); this.Mappable = false; } /** * When passed a Graphics object, this method will then plot an * expanding grid that represents the distance at each pixel. * The major superclass methods are getPixelsFromKm which * uses the gate width and zoom level to determine a distance value * for each pixel. Then the grid is plotted expanding outwards from * the center at an interval determined by the config.getPlotRange() function * which returns km. This grid is drawn with lines and the idea is to * start with the x,y 0,0 coordinates at the bottom left. * * @param g Description of the Parameter */ @Override public void plotGrid(final Graphics g) { if (g == null) return; g.setColor(Color.WHITE); //Used to represent how many km have been plotting representing //distance away and elevation markers. //Start the x,y location at a lower right corner offset by the //panning info. with a starting offset such that the bottom and //side are visible. int startX = getOriginX(); int startY = getOriginY(); //While we haven't yet plotted out to the maximum possible distance, //for the x term, continue to do so. This may be better served with //the y distance but will result in a lot of wasted lines that are //WAY off the screen, though if a user were to pan up a lot, this //would be useful. for (int currDist = 0; //Vertical Lines. currDist <= config.getPlotRange(); currDist += config.getGridSpacing()) { int x = startX + getPixelsFromKmX(currDist); g.drawLine(x, startY - getPixelsFromKmY(config.getMaxPlotHeight()), x, startY); //Label the line g.drawString(String.valueOf(currDist), x, startY + 15); } int maxX = startX + getPixelsFromKmX(config.getPlotRange()); for (int currDist = 0; //Horizontal Lines. currDist <= config.getMaxPlotHeight(); currDist += config.getRHIHVFactor() > 3 ? 1 : 2) { int y = startY - getPixelsFromKmY(currDist); g.drawLine(startX, y, maxX, y); //Label the line g.drawString(String.valueOf(currDist), startX - 15, y); } } @Override public void plotClickPoint(final Graphics g) { if (g == null) return; g.setColor(Color.WHITE); int radius = 10; //pixels int range = getPixelsFromKmX(clickRng) + startOffset + offsetX; double clickZ = Math.tan(Math.toRadians(clickEl)) * clickRng; int height = -getPixelsFromKmY(clickZ) + this.height - startOffset + offsetY; g.drawLine(range - radius, height - radius, range + radius, height + radius); g.drawLine(range + radius, height - radius, range - radius, height + radius); range++; g.drawLine(range - radius, height - radius, range + radius, height + radius); g.drawLine(range + radius, height - radius, range - radius, height + radius); range++; g.drawLine(range - radius, height - radius, range + radius, height + radius); g.drawLine(range + radius, height - radius, range - radius, height + radius); } @Override protected void plotAircraft(final ChillTrackInfo loc) { addAircraftPoint(loc.ident, new Point( getOriginX() + getPixelsFromKmX(Math.sqrt(loc.xKm * loc.xKm + loc.yKm * loc.yKm)), getOriginY() - getPixelsFromKmY(loc.altKm))); System.out.println(loc.toString()); } @Override protected void plotAircraft(final ChillOldExtTrackInfo coeti) { addAircraftPoint(coeti.trackID, new Point( getOriginX() + getPixelsFromKmX(Math.sqrt(coeti.posX * coeti.posX + coeti.posY * coeti.posY)), getOriginY() - getPixelsFromKmY(coeti.altitudeM * 1e-3))); System.out.println(coeti.toString()); } @Override protected void plotAircraft(final ChillNewExtTrackInfo cneti) { addAircraftPoint(cneti.trackID, new Point( getOriginX() + getPixelsFromKmX(Math.sqrt(cneti.posX * cneti.posX + cneti.posY * cneti.posY)), getOriginY() - getPixelsFromKmY(cneti.altitudeM * 1e-3))); System.out.println(cneti.toString()); } @Override protected double getStartAngle(final Ray currRay) { double startDegs = currRay.getStartElevation(); if (startDegs > 180) startDegs -= 360; return Math.toRadians(startDegs); } @Override protected double getEndAngle(final Ray currRay) { double endDegs = currRay.getEndElevation(); if (endDegs > 180) endDegs -= 360; return Math.toRadians(endDegs); } @Override protected int getX(final Angle angle, final double offset) { return getOriginX() + (int) (offset * angle.getCos()); } @Override protected int getY(final Angle angle, final double offset, final int xPos) { int yPos = (int) (25 * this.height * config.getRHIHVFactor() * offset * angle.getSin() / (config.getMaxPlotHeight() * this.width)); return getOriginY() - getPixelsFromKmY(getKmFromPixelsY(yPos) + ViewUtil.getKmElevation(angle.getAngle(), getKmFromPixelsX(xPos))); } @Override protected boolean outOfRange(final int[] xVals, final int[] yVals) { assert xVals.length == yVals.length; double maxDistance = config.getPlotRange(); double maxHeight = config.getMaxPlotHeight(); for (int i = 0; i < xVals.length; ++i) { if (getKmFromPixelsX(xVals[i]) > maxDistance && getKmFromPixelsY(this.height - yVals[i]) > maxHeight) continue; return false; } return true; } @Override public int getOriginX() { return startOffset + offsetX; } @Override public int getOriginY() { return (this.height - startOffset) + offsetY; } @Override public double getRangeInKm(final int x, final int y) { return getKmFromPixelsX(Math.abs(getOriginX() - x)); } @Override public double getElevationInKm(final int x, final int y) { return getKmFromPixelsY(Math.abs(getOriginY() - y)); } @Override public String getPlotMode() { return "RHI"; } @Override protected int getPixelsFromKm(final double distance) { return this.getPixelsFromKmX(distance); } @Override protected double getKmFromPixels(final int distance) { return this.getKmFromPixelsX(distance); } /** * @param distance the distance in km that needs to be found from pixels. * @return The number of pixels that represent this dist in km. */ private int getPixelsFromKmX(final double distance) { return (int) ((this.width * distance * 1e3) / (config.getPlotRange() * 1024)); } /** * @param distance The distance in km that is to be translated to pixels. * @return This returns the pixels * scaling. */ private int getPixelsFromKmY(final double distance) { return (int) ((this.height * 150 * config.getRHIHVFactor() * distance * 1e3) / (config.getMaxPlotHeight() * config.getPlotRange() * 1024 * 3 * 2)); } private double getKmFromPixelsX(final int numPixels) { return (config.getPlotRange() * numPixels * 1024) / (1e3 * this.width); } private double getKmFromPixelsY(final int numPixels) { return (config.getMaxPlotHeight() * numPixels * config.getPlotRange() * 1024 * 3 * 2) / (1e3 * this.height * 150 * config.getRHIHVFactor()); } @Override public double getKmEast(final int x, final int y) { return getKmFromPixelsX(x - getOriginX()) * Math.sin(Math.toRadians(radarAzimuth)); } @Override public double getKmNorth(final int x, final int y) { return getKmFromPixelsX(x - getOriginX()) * Math.cos(Math.toRadians(radarAzimuth)); } @Override public double getAzimuthDegrees(final int x, final int y) { double elevation = getElevationDegrees(x, y); Ray ray = vc.getRayAtEl(this.type, elevation); if (ray == null) return this.radarAzimuth; else return ray.getStartAzimuth(); } @Override public double getElevationDegrees(final int x, final int y) { double u = getElevationInKm(x, y); double r = getRangeInKm(x, y); return Math.toDegrees(Math.atan(u / r)); } @Override public int getPixelsX(final double kmEast, final double kmNorth) { //return getPixelsFromKmX(Math.sqrt(kmEast*kmEast + kmNorth*kmNorth)) * (kmEast > 0 ? 1 : -1); return 0; } @Override public int getPixelsY(final double kmEast, final double kmNorth) { return 0; } /** * based on width (x) only */ @Override public double getPlotStepSize() { return (1024 * config.getPlotRange()) / (metersPerGate * this.width); } @Override public boolean isExportable() { return true; } @Override public void export(final ZipOutputStream zip) throws IOException { OutputStreamWriter out; //write kml double hypotenuse = config.getPlotRange() / 2; double radians = Math.toRadians(radarAzimuth + 90); double x = hypotenuse * Math.cos(radians); double y = hypotenuse * Math.sin(radians); double[] degrees = ViewUtil.getDegrees(x, y); zip.putNextEntry(new ZipEntry("vchillrhi.kml")); try { out = new OutputStreamWriter(zip, "UTF-8"); } catch (UnsupportedEncodingException uee) { out = new OutputStreamWriter(zip); } out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<kml xmlns=\"http://earth.google.com/kml/2.1\">\n" + "<Folder>\n" + " <LookAt>\n" + " <longitude>" + degrees[0] + "</longitude>\n" + " <latitude>" + degrees[1] + "</latitude>\n" + " <range>" + 2 * config.getMaxPlotRange() * 1000 + "</range>\n" + " <tilt>75</tilt>\n" + " <heading>" + (radarAzimuth - 90) + "</heading>\n" + " </LookAt>\n" + " <TimeSpan>\n" + " <begin>" + df.format(this.getBeginDaT()) + "</begin>\n" + " <end>" + df.format(this.getDateAndTime()) + "</end>\n" + " </TimeSpan>\n" + " <name>" + this.getRadarName() + " Radar</name>\n" + " <Placemark>\n" + " <description><![CDATA[Created with <a href=\"http://chill.colostate.edu/java/\">Java VCHILL</a>]]></description>\n" + " <name>" + this.getRadarName() + " Radar Facility</name>\n" + " <LookAt>\n" + " <longitude>" + lm.getLongitude() + "</longitude>\n" + " <latitude>" + lm.getLatitude() + "</latitude>\n" + " <range>500.0</range>\n" + " <tilt>0</tilt>\n" + " <heading>0</heading>\n" + " </LookAt>\n" + " <Point>\n" + " <coordinates>" + lm.getLongitude() + "," + lm.getLatitude() + ",0</coordinates>\n" + " </Point>\n" + " </Placemark>\n" + " <Placemark>\n" + " <name>Radar Data</name>\n" + " <LookAt>\n" + " <longitude>" + degrees[0] + "</longitude>\n" + " <latitude>" + degrees[1] + "</latitude>\n" + " <range>" + 2 * config.getMaxPlotRange() * 1000 + "</range>\n" + " <tilt>75</tilt>\n" + " <heading>" + (radarAzimuth - 90) + "</heading>\n" + " </LookAt>\n" + " <Model>\n" + " <altitudeMode>relativeToGround</altitudeMode>\n" + " <Location>\n" + " <longitude>" + lm.getLongitude() + "</longitude>\n" + " <latitude>" + lm.getLatitude() + "</latitude>\n" + " <altitude>0.0</altitude>\n" + " </Location>\n" + " <Orientation>\n" + " <heading>" + (radarAzimuth - 90) + "</heading>\n" + " <tilt>0</tilt>\n" + " <roll>0</roll>\n" + " </Orientation>\n" + " <Scale>\n" + " <x>1.0</x>\n" + " <y>1.0</y>\n" + " <z>1.0</z>\n" + " </Scale>\n" + " <Link>\n" + " <href>rhiscan.dae</href>\n" + " </Link>\n" + " </Model>\n" + " </Placemark>\n" + " <ScreenOverlay>\n" + " <name>Legend</name>\n" + " <Icon>\n" + " <href>vchillbar.png</href>\n" + " </Icon>\n" + " <overlayXY x=\"0\" y=\"1\" xunits=\"fraction\" yunits=\"fraction\"/>\n" + " <screenXY x=\"0\" y=\"1\" xunits=\"fraction\" yunits=\"fraction\"/>\n" + " </ScreenOverlay>\n" + "</Folder>\n" + "</kml>\n"); out.flush(); zip.closeEntry(); /* Not needed? //write textures zip.putNextEntry(new ZipEntry("textures.txt")); try { out = new OutputStreamWriter(zip, "UTF-8"); } catch (UnsupportedEncodingException uee) { out = new OutputStreamWriter(zip); } out.write("<vchillimg.png> <vchillimg.png>\n"); out.flush(); zip.closeEntry(); */ //write model double startX = 0 - getKmFromPixelsX(getOriginX()); double endX = getKmFromPixelsX(width - getOriginX()); double startY = 0 - getKmFromPixelsY(height - getOriginY()); double endY = getKmFromPixelsY(getOriginY()); zip.putNextEntry(new ZipEntry("rhiscan.dae")); try { out = new OutputStreamWriter(zip, "UTF-8"); } catch (UnsupportedEncodingException uee) { out = new OutputStreamWriter(zip); } out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">\n" + " <asset>\n" + " <unit name=\"km\" meter=\"1000\"/>\n" + " <up_axis>Z_UP</up_axis>\n" + " </asset>\n" + " <library_images>\n" + " <image id=\"vchillimg-image\" name=\"vchillimg-image\">\n" + " <init_from>vchillimg.png</init_from>\n" + " </image>\n" + " </library_images>\n" + " <library_materials>\n" + " <material id=\"vchillimgID\" name=\"vchillimg\">\n" + " <instance_effect url=\"#vchillimg-effect\"/>\n" + " </material>\n" + " </library_materials>\n" + " <library_effects>\n" + " <effect id=\"vchillimg-effect\" name=\"vchillimg-effect\">\n" + " <profile_COMMON>\n" + " <newparam sid=\"vchillimg-image-surface\">\n" + " <surface type=\"2D\">\n" + " <init_from>vchillimg-image</init_from>\n" + " </surface>\n" + " </newparam>\n" + " <newparam sid=\"vchillimg-image-sampler\">\n" + " <sampler2D>\n" + " <source>vchillimg-image-surface</source>\n" + " </sampler2D>\n" + " </newparam>\n" + " <technique sid=\"COMMON\">\n" + " <phong>\n" + " <emission>\n" + " <color>0.0 0.0 0.0 1</color>\n" + " </emission>\n" + " <ambient>\n" + " <color>0.0 0.0 0.0 1</color>\n" + " </ambient>\n" + " <diffuse>\n" + " <texture texture=\"vchillimg-image-sampler\" texcoord=\"UVSET0\"/>\n" + " </diffuse>\n" + " <specular>\n" + " <color>0.0 0.0 0.0 1</color>\n" + " </specular>\n" + " <shininess>\n" + " <float>20.0</float>\n" + " </shininess>\n" + " <reflectivity>\n" + " <float>0.1</float>\n" + " </reflectivity>\n" + " <transparent>\n" + " <color>1 1 1 1</color>\n" + " </transparent>\n" + " <transparency>\n" + " <float>0.0</float>\n" + " </transparency>\n" + " </phong>\n" + " </technique>\n" + " </profile_COMMON>\n" + " </effect>\n" + " </library_effects>\n" + " <library_geometries>\n" + " <geometry id=\"mesh1-geometry\" name=\"mesh1-geometry\">\n" + " <mesh>\n" + " <source id=\"mesh1-geometry-position\">\n" + " <float_array id=\"mesh1-geometry-position-array\" count=\"12\">\n" + " " + startX + "\n" + " 0.0\n" + " " + startY + "\n" + "\n" + " " + endX + "\n" + " 0.0\n" + " " + startY + "\n" + "\n" + " " + startX + "\n" + " 0.0\n" + " " + endY + "\n" + "\n" + " " + endX + "\n" + " 0.0\n" + " " + endY + "\n" + " </float_array>\n" + " <technique_common>\n" + " <accessor source=\"#mesh1-geometry-position-array\" count=\"4\" stride=\"3\">\n" + " <param name=\"X\" type=\"float\"/>\n" + " <param name=\"Y\" type=\"float\"/>\n" + " <param name=\"Z\" type=\"float\"/>\n" + " </accessor>\n" + " </technique_common>\n" + " </source>\n" + " <source id=\"mesh1-geometry-normal\">\n" + " <float_array id=\"mesh1-geometry-normal-array\" count=\"6\">0.0 -1.0 0.0 0.0 1.0 0.0 </float_array>\n" + " <technique_common>\n" + " <accessor source=\"#mesh1-geometry-normal-array\" count=\"2\" stride=\"3\">\n" + " <param name=\"X\" type=\"float\"/>\n" + " <param name=\"Y\" type=\"float\"/>\n" + " <param name=\"Z\" type=\"float\"/>\n" + " </accessor>\n" + " </technique_common>\n" + " </source>\n" + " <source id=\"mesh1-geometry-uv\">\n" + " <float_array id=\"mesh1-geometry-uv-array\" count=\"8\">0.0 0.0 1.0 0.0 0.0 1.0 1.0 1.0 </float_array>\n" + " <technique_common>\n" + " <accessor source=\"#mesh1-geometry-uv-array\" count=\"4\" stride=\"2\">\n" + " <param name=\"S\" type=\"float\"/>\n" + " <param name=\"T\" type=\"float\"/>\n" + " </accessor>\n" + " </technique_common>\n" + " </source>\n" + " <vertices id=\"mesh1-geometry-vertex\">\n" + " <input semantic=\"POSITION\" source=\"#mesh1-geometry-position\"/>\n" + " </vertices>\n" + " <triangles material=\"vchillimg\" count=\"4\">\n" + " <input semantic=\"VERTEX\" source=\"#mesh1-geometry-vertex\" offset=\"0\"/>\n" + " <input semantic=\"NORMAL\" source=\"#mesh1-geometry-normal\" offset=\"1\"/>\n" + " <input semantic=\"TEXCOORD\" source=\"#mesh1-geometry-uv\" offset=\"2\" set=\"0\"/>\n" + " <p>0 0 0 1 0 1 2 0 2 0 1 0 2 1 2 1 1 1 3 0 3 2 0 2 1 0 1 3 1 3 1 1 1 2 1 2 </p>\n" + " </triangles>\n" + " </mesh>\n" + " </geometry>\n" + " </library_geometries>\n" + " <library_visual_scenes>\n" + " <visual_scene id=\"RHIScanScene\" name=\"RHIScanScene\">\n" + " <node id=\"Model\" name=\"Model\">\n" + " <node id=\"mesh1\" name=\"mesh1\">\n" + " <instance_geometry url=\"#mesh1-geometry\">\n" + " <bind_material>\n" + " <technique_common>\n" + " <instance_material symbol=\"vchillimg\" target=\"#vchillimgID\">\n" + " <bind_vertex_input semantic=\"UVSET0\" input_semantic=\"TEXCOORD\" input_set=\"0\"/>\n" + " </instance_material>\n" + " </technique_common>\n" + " </bind_material>\n" + " </instance_geometry>\n" + " </node>\n" + " </node>\n" + " </visual_scene>\n" + " </library_visual_scenes>\n" + " <scene>\n" + " <instance_visual_scene url=\"#RHIScanScene\"/>\n" + " </scene>\n" + "</COLLADA>\n"); out.flush(); zip.closeEntry(); } }