/*
* MapPanel.java
*
* Created on April 9, 2005, 9:38 PM
*
* $Id: MapPanel.java 31 2006-09-16 15:57:20Z gabriel $
*
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Viperfish.
*
* The Initial Developer of the Original Code is Gabriel Galibourg.
* Portions created by Gabriel Galibourg are Copyright (C) 2005-2006
* Gabriel Galibourg. All Rights Reserved.
*
* Contributor(s):
*
*/
package com.solfin.viperfish;
import com.solfin.grib.BadGribException;
import com.solfin.grib.GribFile;
import com.solfin.grib.GribParameter;
import com.solfin.grib.GribRecord;
import com.solfin.grib.GribUnit;
import com.solfin.grib.GribValue;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.text.MessageFormat;
import javax.swing.*;
import javax.swing.event.*;
//import java.net.*;
import java.awt.print.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import com.solfin.tools.*;
/*
* WARNING: the following import statement means I can use mathematical
* functions directly without prefixing them with "Math."
* ie I can now write abs(var) instead of Math.abs(var)
*
* Don't abuse such import statements, it makes code hard to read. I only
* use it for Math.*, and for methods only (not constants).
*/
import static java.lang.Math.*;
public class MapPanel extends JPanel implements MouseInputListener,Printable {
Controller dad;
final static int maxCharHeight = 15;
final static int minFontSize = 6;
FontMetrics fontMetrics;
private BufferedImage bi=null;
private BufferedImage biMap=null;
private AnyProjection proj=null;
private Point2D.Double referencePoint=null;
private Point prevRoutePoint=null;
private Point nextRoutePoint=null; // usefull when you insert a new waypoint between two existing waypoints.
private Point currentRoutePoint=null;
private int currentWaypointIdx=0;
Rectangle2D.Double rectToDraw=null;
Rectangle2D.Double previousRectDrawn = new Rectangle2D.Double();
int offsetX=0;
int offsetY=0;
GribRecord gr1=null;
GribRecord gr2=null;
Dimension oldSize=null;
// mouse popup
JPopupMenu popup;
private final static int AREA_NONE=0;
private final static int AREA_ZOOM=1;
private final static int AREA_MOVE=2;
private final static int AREA_ROUTE=3;
private final static int AREA_ROUTE_INSERT=4;
private final static int AREA_ROUTE_MOVE=5;
int areaFlag=AREA_NONE;
// Some final variables used below ....
private final static int CtrlButton1 = InputEvent.CTRL_DOWN_MASK | InputEvent.BUTTON1_DOWN_MASK;
private final static int CtrlButton2 = InputEvent.CTRL_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK;
private final static int ShiftButton1 = InputEvent.SHIFT_DOWN_MASK | InputEvent.BUTTON1_DOWN_MASK;
public MapPanel(Controller dad) { //, BoundaryBox visible) {
super();
this.dad=dad;
//this.visible=visible;
// add listeners ...
addMouseListener(this);
addMouseMotionListener(this);
//Initialize drawing colors
setBackground(Color.white);
setForeground(Color.black);
//openMapFile(visible.getWidth());
//setMinimumSize(new Dimension(1000,600));
proj=new MercatorProjection(getSize()); //,new MapPoint(0,0),1);
}
//
// Interface methods that must be supplied :
//
// MouseListener methods
public void mouseClicked(MouseEvent e) {} //empty
public void mouseEntered(MouseEvent e) {} // empty
public void mouseExited(MouseEvent e) {} //empty
public void mousePressed(MouseEvent e) {
if (areaFlag==AREA_ROUTE_INSERT) {
dad.getRoute().addBefore(currentWaypointIdx,proj.toMapPoint(new Point(e.getX(),e.getY())));
repaint();
areaFlag=AREA_NONE;
prevRoutePoint=null;
nextRoutePoint=null;
currentRoutePoint=null;
return;
}
if (areaFlag==AREA_ROUTE_MOVE) {
System.err.println("Now move this point!!!!");
dad.getRoute().addBefore(currentWaypointIdx, proj.toMapPoint(new Point(e.getX(),e.getY())));
dad.getRoute().remove(currentWaypointIdx+1);
areaFlag=AREA_NONE;
prevRoutePoint=null;
nextRoutePoint=null;
currentRoutePoint=null;
return;
}
if (e.isPopupTrigger()) {
showPopupMenu(e.getComponent(), e.getX(), e.getY());
return;
}
referencePoint = new Point2D.Double(e.getX(),e.getY());
// CTRL+mouse button 1 => area zoom start
if ((e.getModifiersEx() & CtrlButton1) == CtrlButton1) {
areaFlag=AREA_ZOOM;
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
updateDrawableRect(e.getX(),e.getY());
return;
}
// SHIFT+Mouse button 1 => area move start
if ((e.getModifiersEx() & ShiftButton1) == ShiftButton1) {
areaFlag=AREA_MOVE;
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
return;
}
// CTRL+mouse button 2 => route point to add
if ((e.getModifiersEx() & CtrlButton2) == CtrlButton2) {
areaFlag=AREA_ROUTE;
// Here we recalculate lastRoutePoint from lastRouteMapPoint
prevRoutePoint=proj.toPoint(dad.getRoute().getLastWaypoint());
// if (lastRouteMapPoint!=null) {
// Point2D p=mg.toPoint2D(lastRouteMapPoint);
// lastRoutePoint=new Point2D.Double(p.getX(),p.getY());
// }
currentRoutePoint=new Point(e.getX(),e.getY());
repaint();
//updateRouteLine(e.getX(),e.getY());
return;
}
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
showPopupMenu(e.getComponent(), e.getX(), e.getY());
return;
}
updateLabel(e.getPoint());
// mouse button has been released so reset cursor icon to default
setCursor(Cursor.getDefaultCursor());
offsetX=0;
offsetY=0;
// CRTL+Mouse button 1 => area zoom end
if (areaFlag == AREA_ZOOM) {
areaFlag=AREA_NONE;
// in case mouse moved a lot just as it was released!
updateDrawableRect(e.getX(), e.getY());
referencePoint=null;
if (rectToDraw!=null && rectToDraw.width!=0 && rectToDraw.height!=0) {
proj.zoom(rectToDraw);
forceRepaint();
}
return;
}
// SHIFT+Mouse button 1 => area move end
if (areaFlag == AREA_MOVE) {
areaFlag=AREA_NONE;
offsetX=0;
offsetY=0;
Rectangle2D.Double newRect=new Rectangle2D.Double(
referencePoint.x-e.getX(),referencePoint.y-e.getY(),getWidth(),getHeight());
proj.zoom(newRect);
forceRepaint();
referencePoint=null;
return;
}
// CRTL+Mouse button 2 => route point
if (areaFlag == AREA_ROUTE) {
areaFlag=AREA_NONE;
System.err.println("Adding a route point");
dad.getRoute().add(proj.toMapPoint(new Point(e.getX(),e.getY())));
//lastRoutePoint=new Point2D.Double(e.getX(),e.getY());
//lastRouteMapPoint=mg.toMapPoint(lastRoutePoint);
prevRoutePoint=null;
nextRoutePoint=null;
currentRoutePoint=null;
}
areaFlag=AREA_NONE;
referencePoint=null;
rectToDraw=null;
repaint();
}
public void mouseDragged(MouseEvent e) {
areaFlag=AREA_NONE;
// CTRL+Mouse button 1 => area zoom selection is being made
if ((e.getModifiersEx() & CtrlButton1) == CtrlButton1) {
areaFlag=AREA_ZOOM;
updateLabel(e.getPoint());
updateDrawableRect(e.getX(),e.getY());
return;
}
// SHIFT+Mouse button 1 => area move is being made
if ((e.getModifiersEx() & ShiftButton1) == ShiftButton1) {
areaFlag=AREA_MOVE;
offsetX=(int)(e.getX()-referencePoint.x);
offsetY=(int)(e.getY()-referencePoint.y);
repaint();
return;
}
// CRTL+Mouse button 2 => route point is being set
if ((e.getModifiersEx() & CtrlButton2) == CtrlButton2) {
areaFlag=AREA_ROUTE;
updateLabel(e.getPoint());
currentRoutePoint=new Point(e.getX(), e.getY());
repaint();
//updateRouteLine(e.getX(),e.getY());
return;
}
}
public void mouseMoved(MouseEvent e) {
if (e==null) return;
updateLabel(e.getPoint());
if (popup!=null && popup.isVisible()) {
popup.setVisible(false);
popup=null;
}
if (areaFlag==AREA_ROUTE_INSERT || areaFlag==AREA_ROUTE_MOVE) {
currentRoutePoint=new Point(e.getX(), e.getY());
repaint();
}
}
// Printing methods
PrintRequestAttributeSet aset = null;
private void initASet() {
if (aset == null) {
aset=new HashPrintRequestAttributeSet();
aset.add(OrientationRequested.LANDSCAPE);
aset.add(new Copies(1));
aset.add(new JobName("JMap", null));
}
}
public void pageSetup() {
initASet();
/* Create a print job */
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(this);
/* locate a print service that can handle the request */
PrintService[] services =
PrinterJob.lookupPrintServices();
if (services.length > 0) {
System.out.println("selected printer " + services[0].getName());
try {
pj.setPrintService(services[0]);
pj.pageDialog(aset);
} catch (PrinterException pe) {
System.err.println(pe);
}
}
}
public void print() {
initASet();
/* Create a print job */
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(this);
/* locate a print service that can handle the request */
PrintService[] services =
PrinterJob.lookupPrintServices();
if (services.length > 0) {
System.out.println("selected printer " + services[0].getName());
try {
pj.setPrintService(services[0]);
//pj.pageDialog(aset);
if(pj.printDialog(aset)) {
pj.print(aset);
}
} catch (PrinterException pe) {
System.err.println(pe);
}
}
}
//
// methods centralising calls to the controller
//
private Point mouseLoc=null;
private void updateLabel(Point mLoc) {
// no projection obect: bail out
if (proj==null) return;
// no MapGraphic object bail out
//// if (mg==null) return;
// no mouse location ....
if (mLoc==null) {
// do we have an old mouse location if not bail out
if (mouseLoc==null)
return;
// if we do use it
else
mLoc=mouseLoc;
}
// store mouse location away so it may be reused if necessary
mouseLoc=mLoc;
// now convert mouse position to a map position (ie a latitude+longitude)
MapPoint mouseMP=proj.toMapPoint(mLoc);
// now prepare to build label string
String labelStr="";
labelStr += mouseMP.getLatitude().toString();
labelStr += " "+mouseMP.getLongitude().toString();
if (gr1!=null && gr2!=null && gr1.getParameterId()==33 && gr2.getParameterId()==34) {
double u=gr1.getValueAt(mouseMP);
double v=gr2.getValueAt(mouseMP);
double windSpeed=sqrt(u*u+v*v) * 1.9438444908;
float theta = (float)Longitude.correctLongitude(360 - 180.0 / Math.PI * atan2(v,u) - 90);
labelStr+=" "+MessageFormat.format(
dad.getBundle().getString("Message.statusWindLabel"),
theta,
windSpeed);
} else if (gr1!=null) {
GribParameter gpr=new GribParameter(gr1.getParameterId());
String grLabel=gpr.getLabel();
GribUnit gu=gpr.getUnitType();
if (grLabel==null)
grLabel="{0}";
if (gu!=null) {
labelStr+=" "+MessageFormat.format(grLabel,gu.fromSI(gr1.getValueAt(mouseMP)),gu.getDisplay());
} else {
labelStr+=" "+MessageFormat.format(grLabel,gr1.getValueAt(mouseMP),gpr.getUnit());
}
// if (gr1.getParameterId()==11)
// labelStr+=" "+MessageFormat.format(
// dad.getBundle().getString("Message.statusTempLabel"),
// ReadTools.Temperature.C.fromSI(gr1.getValueAt(mouseMP)));
// else
// labelStr+=" "+MessageFormat.format(
// dad.getBundle().getString("Message.statusValueLabel"),
// gr1.getValueAt(mouseMP));
}
dad.updateLabel(labelStr);
}
public AnyProjection getProjection() {
return proj;
}
public MapPoint getMouseMapPos() {
return (mouseLoc==null? new MapPoint() : proj.toMapPoint(mouseLoc) );
//double[] ll=mg.convertXYtoLL(mouseLoc.x,mouseLoc.y);
//return new MapPoint(ll[0],ll[1]);
}
public Point getMousePos() {
return mouseLoc;
}
public void zoomIn() {
proj.zoom(0.1);
forceRepaint();
}
public void zoomOut() {
proj.zoom(-0.1);
forceRepaint();
}
public void zoomReset() {
referencePoint=null;
rectToDraw=null;
proj.zoomReset();
forceRepaint();
}
//
// methods called by the controller
//
protected void setGRIBData(File gribFile,int recordId1, int recordId2) {
// this.gribFile = gribFile;
try {
GribFile gf=new GribFile(gribFile);
gr1 = gf.getRecord(recordId1);
gr2 = recordId2>=0 ? gf.getRecord(recordId2) : null;
} catch (BadGribException e) {
System.err.println(e);
gr1=null;
gr2=null;
//gf=null;
}
//this.gribRecordId1=recordId1;
//this.gribRecordId2=recordId2;
bi=null;
referencePoint=null;
rectToDraw=null;
repaint();
}
//
// support methods needed by the other methods
// they generaly can remain private
//
private void showPopupMenu(Component c, int x, int y) {
AppMenu amenu=new AppMenu(dad.getBundle(), dad.getActionMap());
// determine if we are near a waypoint of the route
Route route=dad.getRoute();
int numPoints=route.getNbValues();
double minDist=route.closestDistance(new Point(x,y),proj);
// show a 'waypoint' popup menu if we are within 6 pixels of a waypoint center
if (minDist < 6) {
popup=amenu.createPopupMenu("PopupWaypoint");
}
// otherwise show the 'standard' popup menu
else {
popup=amenu.createPopupMenu("Popup");
}
popup.show(c, x-10, y-10);
}
public void changeBoundaryBox(BoundaryBox newVisible) {
System.err.println("I'm in changeBoundaryBox!!! "+newVisible.toString());
//// visible = new BoundaryBox(newVisible);
//// System.err.println("Middle is in"+visible.getMiddle().toString());
// force recreation of map
bi=null;
biMap=null;
//proj=null;
//// mg=null;
referencePoint=null;
rectToDraw=null;
prevRoutePoint=null;
nextRoutePoint=null;
currentRoutePoint=null;
}
public void forceRepaint() {
bi=null;
biMap=null;
//// mg=null;
repaint();
}
private MapFile openMapFile() {
MapFile fMap=null;
try {
fMap= new MapFile((float)proj.getAreaThreshold()*360.0F); //visible.getWidth());
} catch (java.io.FileNotFoundException e) {
System.err.println("FileNotFoundException: " + e.getMessage());
throw new RuntimeException(e);
} catch (java.io.IOException e) {
System.err.println("IOException: " + e.getMessage());
throw new RuntimeException(e);
}
// we need this dummy call to trigger initialisation
// inside the MapFile class.
int nbPoly = fMap.getNbPolygons();
return fMap;
}
private void updateDrawableRect(int mouseX,int mouseY) {
if (referencePoint==null) {
referencePoint = new Point2D.Double(mouseX,mouseY);
}
int compWidth = getWidth();
int compHeight = getHeight();
double x = referencePoint.x;
double y = referencePoint.y;
double width = mouseX-referencePoint.x;
double height = mouseY-referencePoint.y;
//Make the width and height positive, if necessary.
if (width < 0) {
width = 0 - width;
x = x - width + 1;
if (x < 0) {
width += x;
x = 0;
}
}
if (height < 0) {
height = 0 - height;
y = y - height + 1;
if (y < 0) {
height += y;
y = 0;
}
}
// Keep proportion between width and height to the same ratio
// as is already between compWidth/compHeight
double widthHeightRatio=(double)compWidth/compHeight;
if (height!=0) {
if (width/height > widthHeightRatio) {
height = (int)(width / widthHeightRatio);
} else if (width/height < widthHeightRatio) {
width = (int)(height * widthHeightRatio);
}
}
//The rectangle shouldn't extend past the drawing area.
if ((x + width) > compWidth) {
width = compWidth - x;
height = (int)(width / widthHeightRatio);
}
if ((y + height) > compHeight) {
height = compHeight - y;
width = (int)(height * widthHeightRatio);
}
//Update rectToDraw after saving old value.
if (rectToDraw != null) {
previousRectDrawn.setRect(
rectToDraw.x, rectToDraw.y,
rectToDraw.width, rectToDraw.height);
rectToDraw.setRect(x, y, width, height);
} else {
rectToDraw = new Rectangle2D.Double(x, y, width, height);
}
// Repaint part so as to show selection rectangle
Rectangle2D.Double totalRepaint = (Rectangle2D.Double)rectToDraw.createUnion(previousRectDrawn);
repaint((int)totalRepaint.x, (int)totalRepaint.y,
(int)totalRepaint.width, (int)totalRepaint.height);
// repaint();
}
private void updateRouteLine(int mouseX, int mouseY) {
// if (lastRoutePoint==null) {
// drawCircle(new Point(mouseX,mouseY), 0.1);
// }
}
public void clearRoute() {
prevRoutePoint=null;
currentRoutePoint=null;
nextRoutePoint=null;
repaint();
}
public void waypointInsertBefore() {
Route route=dad.getRoute();
currentWaypointIdx=route.closest(mouseLoc,proj);
nextRoutePoint=route.getWaypoint(currentWaypointIdx,proj);
prevRoutePoint=(currentWaypointIdx>0 ? route.getWaypoint(currentWaypointIdx-1,proj) : null);
currentRoutePoint=mouseLoc;
areaFlag=AREA_ROUTE_INSERT;
}
public void waypointMove() {
Route route=dad.getRoute();
currentWaypointIdx=route.closest(mouseLoc,proj);
nextRoutePoint=(currentWaypointIdx<route.getNbValues()-1 ? route.getWaypoint(currentWaypointIdx+1,proj) : null);
prevRoutePoint=(currentWaypointIdx>0 ? route.getWaypoint(currentWaypointIdx-1,proj) : null);
currentRoutePoint=mouseLoc;
areaFlag=AREA_ROUTE_MOVE;
}
private FontMetrics pickFont(Graphics2D g2, String longString, int xSpace) {
boolean fontFits = false;
Font font = g2.getFont();
FontMetrics fontMetrics = g2.getFontMetrics();
int size = font.getSize();
String name = font.getName();
int style = font.getStyle();
while ( !fontFits ) {
if ( (fontMetrics.getHeight() <= maxCharHeight)
&& (fontMetrics.stringWidth(longString) <= xSpace) ) {
fontFits = true;
} else {
if ( size <= minFontSize ) {
fontFits = true;
} else {
g2.setFont(font = new Font(name,
style,
--size));
fontMetrics = g2.getFontMetrics();
}
}
}
return fontMetrics;
}
//
// All the drawing methods
//
private void drawWindBarbs(Graphics2D g, AnyProjection proj, GribRecord Ugr, GribRecord Vgr) {
if (Ugr!=null && Vgr!=null) {
// do some routine checks (Ugr and Vgr should be same date, same number of values, span the same area, same di/dj, ....)
//
// Ugr and Vgr are ok .... we now continue to build wind vectors from these two Grib records.
//
// some graphical settings ...
g.setColor(Color.blue);
Iterator<GribValue> Ugvi = Ugr.iterator();
Iterator<GribValue> Vgvi = Vgr.iterator();
GribValue Ugv;
GribValue Vgv;
int iVal=1;
while ( (Ugv=Ugvi.next())!=null && (Vgv=Vgvi.next())!=null ) {
if (!Ugv.isMissing()) {
iVal++;
//mg.drawWindBarb(Ugv.getLongitude(),Ugv.getLatitude(),(float)Ugv.getValue(),(float)Vgv.getValue(), Ugr.getDi());
proj.drawWindBarb(g, Ugv, (float)Ugv.getValue(),(float)Vgv.getValue(), Ugr.getDi());
}
}
}
}
private static double min3(double d1,double d2,double d3) {
return min(min(d1,d2),d3);
}
private static double max3(double d1,double d2,double d3) {
return max(max(d1,d2),d3);
}
private void drawContourInTriangle(Graphics2D g, AnyProjection proj, GribValue[] mpArr, double[] levels) {
// sort the points so smallest value is in [0] largest in [2].
// this is a bubble sort - but only three values to sort!
for (int i=0 ; i<3 ; ++i) {
for (int j=i+1 ; j<3 ; ++j) {
if (mpArr[i].getValue() > mpArr[j].getValue()) {
GribValue t = new GribValue(mpArr[i]);
mpArr[i] = mpArr[j];
mpArr[j] = t;
}
}
}
double minLon=min3(mpArr[0].getLongitude().value(), mpArr[1].getLongitude().value(), mpArr[2].getLongitude().value() );
double maxLon=max3(mpArr[0].getLongitude().value(), mpArr[1].getLongitude().value(), mpArr[2].getLongitude().value() );
double bot=min3(mpArr[0].getValue(), mpArr[1].getValue(), mpArr[2].getValue() );
double top=max3(mpArr[0].getValue(), mpArr[1].getValue(), mpArr[2].getValue() );
double[] x=new double[3];
double[] y=new double[3];
double[] value=new double[3];
double maxLevelVal=levels[levels.length-1];
for (int i=0 ; i<3 ; ++i) {
x[i]=mpArr[i].getLongitude().value() - (maxLon>180 && minLon<180 && mpArr[i].getLongitude().value()>180 ? 360.0 : 0.0);
y[i]=mpArr[i].getLatitude().value();
value[i]=mpArr[i].getValue();
if (value[i]>maxLevelVal)
value[i]=maxLevelVal;
}
double[] cx = new double[3];
double[] cy = new double[3];
int iLevel;
for (iLevel=0 ; iLevel<levels.length && levels[iLevel]<top ; ++iLevel) ;
if (iLevel>=levels.length)
iLevel=levels.length-1;
for ( ; iLevel>0 ; --iLevel){
double levelVal=levels[iLevel];
// if this contour level is lower than lowest point than we can
// leave this method
if (levelVal<bot)
return;
java.util.List<MapPoint> fillPoly=new ArrayList<MapPoint>(3);
fillPoly.add(mpArr[0]);
// find the contour points
double cz;
int iInter=0;
// this triangle cuts accross contour level, we now need to find
// out where on the triangle this takes place.
// we know that of the three segments that make a triangle only
// two will be cut by the contour level - we thus need to find which
// two it is.
// does it intersect between point 0 & 1
cz = (levelVal - value[0])/(value[1] - value[0]);
if ( cz>=0 && cz<=1) {
cx[iInter] = ( x[0] + ( x[1] - x[0] ) * cz );
cy[iInter] = ( y[0] + ( y[1] - y[0] ) * cz );
fillPoly.add(new MapPoint(cx[iInter],cy[iInter]));
++iInter;
}
// does it intersect between point 0 & 2?
cz = (levelVal - value[0])/(value[2] - value[0]);
if ( cz>=0 && cz<=1) {
cx[iInter] = ( x[0] + ( x[2] - x[0] ) * cz );
cy[iInter] = ( y[0] + ( y[2] - y[0] ) * cz );
fillPoly.add(new MapPoint(cx[iInter],cy[iInter]));
++iInter;
}
// does it intersect between point 1 & 2?
cz = (levelVal - value[1])/(value[2] - value[1]);
if (cz>=0 && cz<=1) {
cx[iInter] = ( x[1] + ( x[2] - x[1] ) * cz );
cy[iInter] = ( y[1] + ( y[2] - y[1] ) * cz );
fillPoly.add(new MapPoint(cx[iInter],cy[iInter]));
fillPoly.add(mpArr[1]);
++iInter;
}
// change the color
// hue to go from yellow to red (via green and blue )
//float hue=(float)((310.0*iLevel/levels.length+50.0)/360.0);
// Hue for blue to red color only.
float hue=(float)((120.0*iLevel/levels.length+240.0)/360.0);
if (hue>1.0F) hue=1.0F;
Color levelColor = Color.getHSBColor( hue ,1.0F, Config.getAlpha());
//levelColor = new Color(levelColor.getRed(), levelColor.getGreen(), levelColor.getBlue(), 120);
g.setColor(levelColor);
//g.setXORMode(Color.white);
// draw the contour.
if (iInter==0) {
fillPoly.add(mpArr[1]);
fillPoly.add(mpArr[2]);
proj.fill(g,fillPoly);
} else if (iInter==1) {
// actually we sould never come here!
System.err.println("iInter=1!!! strange!!");
} else {
proj.fill(g,fillPoly);
}
} // end of loop on iLevel
}
private void drawContoursInQuad(Graphics2D g, AnyProjection proj, GribValue[] p,double[] levels) {
// determine if min/max span accross several levels. If that is not the
// case then subdividing the quad into triangles is useless since no
// contours cross this quad in the first place!
double maxValue=Double.MIN_VALUE;
double minValue=Double.MAX_VALUE;
int minLevel=levels.length;
int maxLevel=0;
for (int iLevel=levels.length-1 ; iLevel>=0 ; --iLevel) {
if (minValue < levels[iLevel]) minLevel = iLevel;
if (maxValue < levels[iLevel]) maxLevel = iLevel;
}
if (minLevel == maxLevel) {
// draw an empty polygon of the right color
// its actually unclean to have this code in MapPanel! it should be
// in MapGraphics!
g.setColor(Color.getHSBColor( (float)((310.0*minLevel/levels.length+50.0)/360.0) ,1.0F,1.0F));
java.util.List<MapPoint> fillPoly=new ArrayList<MapPoint>(4);
for (int k=0 ; k<4 ; ++k)
if (!p[k].isMissing())
fillPoly.add(new MapPoint(p[k].getLongitude().value(),p[k].getLatitude().value()));
proj.fill(g, fillPoly);
return;
}
// Calculate the mid point of above four points
int nbVal=0;
double avgValue=0;
double avgLat=0;
double avgLon=0;
double sumLonU=0;
double sumLonV=0;
for (int k=0 ; k<4 ; ++k) {
if (!p[k].isMissing()) {
++nbVal;
double val = p[k].getValue();
maxValue = (val > maxValue ? val : maxValue);
minValue = (val < minValue ? val : minValue);
avgValue += val;
avgLat += p[k].getLatitude().value();
double rLon = toRadians(p[k].getLongitude().value());
sumLonU += cos(rLon);
sumLonV += sin(rLon);
}
}
// Difficult to make triangles from 0,1 or 2 points. So bail out
// early if that happens and save some CPU power.
if (nbVal<=2)
return;
avgValue /= nbVal;
avgLat /= nbVal;
avgLon = (180.0/PI * atan2(sumLonV,sumLonU));
// Now loop for each triangle (4 at most) and call the contourTriangle routine
for (int k=0 ; k<4 ; ++k) {
int i0=k;
int i1=(k+1)%4;
// if one of the points of the quad are missing move to next sub-triangle.
if (nbVal!=4 && (p[i0].isMissing() || p[i1].isMissing()))
continue;
GribValue[] mpArr=new GribValue[3];
// mpArr[0]=new MapPoint(p[i0].getLongitude() , p[i0].getLatitude(), p[i0].getValue());
// mpArr[1]=new MapPoint(p[i1].getLongitude() , p[i1].getLatitude(), p[i1].getValue());
// mpArr[2]=new MapPoint(avgLon, avgLat, avgValue);
mpArr[0]=p[i0];
mpArr[1]=p[i1];
mpArr[2]=new GribValue(avgValue, avgLon, avgLat);
drawContourInTriangle(g, proj, mpArr, levels);
}
//return ;
}
private void drawContourLines(Graphics2D g, AnyProjection proj, GribRecord gr) {
System.out.println("Going to draw contour lines for:"+gr.getParameter().getShortStr());
double min;
double max;
double step;
int nbLevels=30;
GribParameter gp=new GribParameter(gr.getParameterId());
min=gp.getMin();
max=gp.getMax();
step=gp.getStep();
if (min==max) {
min=gr.getMinimumValue();
max=gr.getMaximumValue();
}
System.out.println("Min/max values="+min+" "+max);
// build array of levels
if (step!=0.0) {
nbLevels=(int)((max-min)/step+1);
} else {
nbLevels=30;
step=(max-min)/(nbLevels-1);
}
double[] levels = new double[nbLevels];
for (int ii=0 ; ii<levels.length ;++ii) {
levels[ii]=min+step*ii;
}
for (int ii=0 ; ii<levels.length ;++ii) {
System.out.printf("%d: %f\n",ii,levels[ii]);
}
//mg.setColor(Color.red); //blue);
GribValue[][] gvs=gr.getAllValues();
System.out.println("Ni*Di="+gr.getNi()*gr.getDi());
for (int j=gr.getNj()-2 ; j>=0 ; --j) {
for (int i=gr.getNi()-2 ; i>=0 ; --i) {
GribValue[] p = new GribValue[4];
p[0]=gvs[j][i];
p[1]=gvs[j][i+1];
p[2]=gvs[j+1][i+1];
p[3]=gvs[j+1][i];
drawContoursInQuad(g,proj,p,levels);
}
}
if ((gr.getNi()*gr.getDi()) == 360.0) {
int ni=gr.getNi()-1;
for (int j=gr.getNj()-2 ; j>=0 ; --j) {
GribValue[] p = new GribValue[4];
p[0]=gvs[j][ni];
p[1]=gvs[j][0];
p[2]=gvs[j+1][0];
p[3]=gvs[j+1][ni];
drawContoursInQuad(g,proj,p,levels);
}
}
}
private void drawRoute(Graphics2D g, AnyProjection proj) {
MapPoint[] mp=dad.getRoute().getAllPoints();
//mg.g.setPaint(Color.red);
//mg.g.setXORMode(Color.white); //Color
g.setColor(Color.magenta);
Point prevPoint=null;
if (mp.length>0) {
proj.drawCircle(g,mp[0],-7);
for (int i=1 ; i<mp.length ; ++i) {
proj.drawCircle(g,mp[i],-7);
proj.drawGreatCircleLine(g,mp[i-1],mp[i]);
}
// prevPoint=proj.toPoint(mp[0]);
// //mg.fillOval(mp[0], 7,7);
// g2.fillOval(prevPoint.x-3,prevPoint.y-3,7,7);
}
// MapPoint[] mp=dad.getRoute().getAllPoints();
//
// //mg.g.setPaint(Color.red);
// //mg.g.setXORMode(Color.white); //Color
// g2.setColor(Color.magenta);
//
// Point prevPoint=null;
// if (mp.length>0) {
// prevPoint=proj.toPoint(mp[0]);
// //mg.fillOval(mp[0], 7,7);
// g2.fillOval(prevPoint.x-3,prevPoint.y-3,7,7);
//
// }
// Point nextPoint=null;
// for (int i=1 ; i<mp.length ; ++i) {
// nextPoint=proj.toPoint(mp[i]);
// g2.fillOval(nextPoint.x-3,nextPoint.y-3, 7, 7);
// g2.drawLine(prevPoint.x, prevPoint.y, nextPoint.x, nextPoint.y);
// prevPoint=nextPoint;
// //mg.drawStraightLine(mp[i-1],mp[i],true);
// }
}
public void drawMap(final Graphics2D g, final MapFile fMap, final AnyProjection proj) {
g.setColor(Color.black);
Iterator<MapPolygon> iPol = fMap.iterator( new MapPolygonFilter() {
public boolean accept(MapPolygon mp) {
// we consider this polygon to be of interest only if
// - it is land
// - if it is not land it is big enough to be more than
// a simple dot on the map(eliminate small ponds!)
// - finally the map polygon intersects our display area
return (mp.getLevel()<=1 || mp.getArea()>(1800.0*proj.getAreaThreshold())) &&
proj.isBoundaryBoxVisible(mp.getExtent());
}
});
while (iPol.hasNext()) {
MapPolygon curPol=iPol.next();
MapPoint oldPoint=null;
for (MapPoint newPoint : curPol) {
if (oldPoint!=null) {
//if (visible.intersects(oldPoint,newPoint))
//drawStraightLine(oldPoint, newPoint,true);
//System.err.println("drawing line between "+oldPoint+" and "+newPoint);
proj.drawRhumbLine(g,oldPoint,newPoint);
}
oldPoint=newPoint;
}
}
}
//
// overriden methods:
//
public Dimension getMinimumSize() {
return new Dimension((int)360,(int)279);
}
public void paintComponent(Graphics g) {
System.out.println("in paintComponent()");
//System.out.println("isOpaque "+isOpaque());
//System.out.println("isDoubleBuffered "+isDoubleBuffered());
super.paintComponent(g);
if (referencePoint==null) {
rectToDraw=null;
}
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
//System.out.println(g2.getClipBounds());
Dimension d = getSize();
if (!d.equals(oldSize)) {
System.out.println("Size has changed");
proj.screenResize(d);
oldSize=d;
bi=null;
biMap=null;
referencePoint=null;
rectToDraw=null;
}
//g.clearRect(0,0,(int)d.width,(int)d.height);
//fontMetrics = pickFont(g2, "Filled and Stroked GeneralPath",20);
// build a 3D effect frame around drawing
//g2.setPaint(Color.lightGray);
//g2.draw3DRect(0, 0, d.width - 1, d.height - 1, true);
//g2.draw3DRect(3, 3, d.width - 7, d.height - 7, false);
//g2.setPaint(Color.black);
if (bi==null) {
double w=d.getWidth();
double h=d.getHeight();
bi = new BufferedImage((int)w,(int)h,BufferedImage.TYPE_INT_ARGB);//createImage(w,h);
Graphics2D big = bi.createGraphics();
// draw the land boundaries
big.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
big.setBackground(Color.getHSBColor(0.0f, 0.0f, Config.getAlpha()));
big.clearRect(0,0,(int)w,(int)h);
// draw GRIB data (1st)
// This first call is only necessary to draw shaded contour lines,
// as we prefer to draw everything else ABOVE it (eg land limits,
// lat-lon grid, etc ...)
if (gr1!=null && gr2==null) {
//AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.DST_OVER); //CLEAR,0.5f);
//big.setComposite(ac);
drawContourLines(big,proj,gr1);
}
// Now load image - the optimal image size (for zoom level) will be loaded by the
// openMapFile() method.
// The world map is loaded in its own buffered image to avoid having to reload it each time.
if (biMap==null) {
biMap = new BufferedImage((int)w,(int)h,BufferedImage.TYPE_INT_ARGB);//createImage(w,h);
Graphics2D bigMap = biMap.createGraphics();
//MapGraphics mgMap = new MapGraphics(bigMap,getSize(),visible);
//mgMap.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
//Color tBg2=new Color(100,0,0,0);
bigMap.setBackground(new Color(0.0f,0.0f,0.0f,0.0f)); //getBackground());
g.clearRect(0,0,(int)w, (int)h);
//bigMap.clear();
MapFile fMap = openMapFile();
drawMap(bigMap,fMap,proj);
fMap = null; //force gc
// TODO: ideally draw the political borders too ....
// draw the longitude/latitude grid
proj.drawGrid(bigMap,true);
//mgMap=null;
bigMap=null;
//biMap=null;
}
// now place biMap on bi
//big.setXORMode(Color.white);
//bi.setData(biMap.getRaster());
Composite acOrig = big.getComposite();
Composite ac1 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
big.setComposite(ac1);
big.drawImage(biMap,0,0,this);
big.setComposite(acOrig);
// draw GRIB data (2nd).
// This second call to GRIB data display is necessary if the data
// being displayed are wind barbs as in that case it is better for
// the barbs to be drawn ABOVE all other data.
if (gr1!=null && gr2!=null && gr1.getParameterId()==33 && gr2.getParameterId()==34) {
drawWindBarbs(big,proj,gr1,gr2);
}
// if we have GRIB data then print out some information on it.
if (gr1!=null) {
big.clearRect(0,0,400,50);
big.setColor(Color.red); //blue);
big.drawString(dad.getBundle().getString("Message.referenceDate")+Config.dateToString(gr1.getReferenceDate()),10,15);
Calendar aCal=gr1.getForecastDate1();
if (aCal!=null) {
big.drawString(dad.getBundle().getString("Message.forecastDate")+Config.dateToString(aCal),10,30);
}
}
updateLabel(null);
}
g2.drawImage(bi, offsetX, offsetY, this);
// Here we draw loaded route
if (offsetX==0 && offsetY==0) {
drawRoute(g2,proj);
}
// if we are zooming on a select area then 'rectToDraw' will
// not be null - draw that rectangle ....
if (rectToDraw != null) {
//Draw a rectangle on top of the image.
g2.setPaint(Color.red);
g2.setXORMode(Color.white); //Color of line varies depending on image colors
g2.drawRect((int)rectToDraw.x, (int)rectToDraw.y,
(int)rectToDraw.width - 1, (int)rectToDraw.height - 1);
}
// if we are setting a route draw this intermediate point
if (areaFlag==AREA_ROUTE || areaFlag==AREA_ROUTE_INSERT || areaFlag==AREA_ROUTE_MOVE) {
g2.setPaint(Color.red);
g2.setXORMode(Color.white); //Color of line varies depending on image colors
if (prevRoutePoint!=null) {
proj.drawGreatCircleLine(g2,proj.toMapPoint(prevRoutePoint),proj.toMapPoint(currentRoutePoint));
//g2.drawLine(prevRoutePoint.x,prevRoutePoint.y, currentRoutePoint.x,currentRoutePoint.y);
}
if (nextRoutePoint!=null) {
proj.drawGreatCircleLine(g2,proj.toMapPoint(nextRoutePoint),proj.toMapPoint(currentRoutePoint));
//g2.drawLine(nextRoutePoint.x,nextRoutePoint.y, currentRoutePoint.x,currentRoutePoint.y);
}
g2.fillOval((int)currentRoutePoint.x-3, (int)currentRoutePoint.y-3, 6,6);
}
// Here we draw some boat information (position, heading)
// drawBoatData();
}
public int print(Graphics g,PageFormat pf,int pageIndex) {
System.out.println("PRINTING!");
// if (pageIndex == 0) {
// System.out.println("iX="+pf.getImageableX()+" iY="+pf.getImageableY()+
// " iH="+pf.getImageableHeight()+" iW="+pf.getImageableWidth()+
// " h="+pf.getHeight()+" w="+pf.getWidth());
// Graphics2D g2d= (Graphics2D)g;
//
//
// MapGraphics mg = new MapGraphics(g2d,new Dimension((int)pf.getWidth(),(int)pf.getHeight()),visible);
//
// BasicStroke wideStroke = new BasicStroke(0.1f);
//
// g2d.setStroke(wideStroke);
// mg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
// mg.setBackground(getBackground());
// mg.clear();
//
//
// // If we need to draw contour lines then do it now ...
// if (gr1!=null && gr2==null) {
// drawContourLines(mg,gr1);
// }
//
// // Now load image - the optimal image size (for zoom level) will be loaded by the
// // openMapFile() method.
// MapFile fMap = openMapFile();
// mg.drawMap(fMap,proj,g2d);
// fMap = null; //force gc
// // draw the longitude/latitude grid
// mg.drawGrid(true);
//
// // if we need to draw wind barbs do it now!
// if (gr1!=null && gr2!=null && gr1.getParameterId()==33 && gr2.getParameterId()==34) {
// drawWindBarbs(mg,gr1,gr2);
// }
//
//
// g2d.setColor(Color.red);
// // g2d.drawString("example string", 250, 250);
// g2d.drawRect(0, 0, (int)pf.getWidth(), (int)pf.getHeight());
//
// return Printable.PAGE_EXISTS;
// } else {
// return Printable.NO_SUCH_PAGE;
// }
return Printable.NO_SUCH_PAGE;
}
}