Polar(极坐标)投影--主要用于天气雷达图


Polar.java
001 /*
002 
003     Polar 投影(扫描方式,自正北方向顺时针)
004 
005        PACKAGE: cma.common.projection
006       FILENAME: Polar.java
007       LANGUAGE: Java2 v1.4
008       ORIGINAL: 无
009    DESCRIPTION: 极坐标投影(主要用于雷达图像处理)
010        RELATED: cma.common.projection.Lambert(兰勃特投影)
011         EDITOR: UltraEdit-32 v12.20a(Windows) NEdit(Linux)
012         CREATE: 2007-05-06 20:08:23
013         UPDATE: 2007-07-18 修改为抽象类Coordinate的扩展类
014         AUTHOR: 刘泽军 ([email protected])
015                 广西气象减灾研究所
016                 Guangxi Institude of Meteorology and Disaster-reducing Research(GIMDR)
017 
018       Compile : javac Coordinate.java Polar.java
019 
020    How to use Polar class:
021 
022    Polar polar = new Polar(109.24, 24.35, 512, 384, 1.0, 0.0);//构造函数
023    ...
024 
025  */
026 
027 /**
028  *
029  *         扫描平面
030  *            /
031  *           /
032  *          /
033  *         /
034  *        /
035  *       /  仰角
036  *      -------------------- 0度平面
037  *
038  * 如图所示:
039  *          扫描平面=>0度平面,需要乘以cos(仰角)
040  *          0度平面=>扫描平面,需要除以cos(仰角)
041  *
042  * 注意,日常显示的雷达图是扫描平面上的图。本类所说的屏幕指扫描平面。
043  *
044  */
045 
046 /**
047  * 雷达扫描示意图
048  *
049  *                    359 0
050  *                        |     radius
051  *                        |       /
052  *                        |      /
053  *                        |angle/
054  *                        |    /
055  *                        | ^ /
056  *                        |  /
057  *                        | /
058  *                        |/
059  * 270 -----------------中心----------------- 90
060  *                        |
061  *                        |
062  *                        |
063  *                        |
064  *                        |
065  *                        |
066  *                        |
067  *                        |
068  *                        |
069  *                       180
070  */
071 
072 package cma.common.projection;
073 
074 import java.awt.*;
075 import java.awt.geom.*;
076 import java.lang.Math.*;
077 
078 public class Polar extends Coordinate {
079 
080     //私有成员
081     private double  perKilometer        = 1.0;      //比例尺:一公里对应的像素点数(扫描平面)
082     private double  elevation           = 0.0;      //仰角
083     private double  cosineElevation     = 1.0;      //仰角的余弦值
084     private double  kmPerDegreeX        = 1.0;      //1经度对应的距离(公里),不同纬度数值不同
085     private double  kmPerDegreeY        = 1.0;      //1纬度对应的距离(公里),不同纬度数值不同
086 
087 /**
088  * 功能:计算球面上两点间的距离(单位:公里),原在edu.gimdr.Atmos.Meteorology类中写有,为避免import过多的类,故重写一份
089  * 参数:
090  *  lon1,lat1   - 第1点的位置(经纬度)
091  *  lon2,lat2   - 第2点的位置(经纬度)
092  * 返回值:
093  *      球面距离
094  */
095     public static double distanceOfSphere(double lon1, double lat1, double lon2, double lat2) {
096         /*  公式:
097             A(x,y)  B(a,b)
098             AB点的球面距离=R*{arccos[cos(b)*cos(y)*cos(a-x)+sin(b)*sin(y)]}, by Google
099         */
100 
101         double  rlon1   = Math.toRadians(lon1);
102         double  rlat1   = Math.toRadians(lat1);
103         double  rlon2   = Math.toRadians(lon2);
104         double  rlat2   = Math.toRadians(lat2);
105 
106         return(Coordinate.RADIUS*(Math.acos(Math.cos(rlat2)*Math.cos(rlat1)*Math.cos(rlon2-rlon1)+Math.sin(rlat2)*Math.sin(rlat1))));
107     }
108 
109 /**
110  * 功能:
111  *      重置参数
112  * 参数:
113  *      lon,lat     - 中心经纬度,
114  *      px,py       - 中心经纬度对应的屏幕坐标
115  *      sc          - 缩放系数
116  *      agl         - 仰角
117  * 返回值:
118  *      无
119  */
120     public void reset(double lon, double lat, int px, int py, double sc, double agl) {
121         type        = Coordinate.POLAR;
122         center          = new Point2D.Double(
123                             lon <   0.0 ?   0.0 : lon > 360.0 360.0 : lon,
124                             lat < -90.0 ? -90.0 : lat >  90.0 ?  90.0 : lat
125                         );
126         place           = new Point(px, py);
127         elevation       = Math.toRadians(Math.IEEEremainder(Math.abs(agl)90.0));//在0-90度之间,但不能为90度
128         cosineElevation = Math.cos(elevation);//仰角的余弦值
129         scale           = sc == 0.0 1.0 : Math.abs(sc);//缩放系数
130         scaleOriginal   = scale;
131         offset          = new Point(00);
132 
133         perKilometer    = 1.0;//标准比例尺
134         //中心经纬度或仰角发生改变,必须重新计算经向和纬向的1度对应的球面距离
135         kmPerDegreeX    = distanceOfSphere(center.x, center.y, center.x+1.0, center.y/ cosineElevation;
136         kmPerDegreeY    = distanceOfSphere(center.x, center.y, center.x, center.y+1.0/ cosineElevation;
137     }
138 
139 /**
140  * 功能:构造函数
141  * 参数:
142  *      lon     - 中心对应的经度坐标
143  *      lat     - 中心对应的纬度坐标
144  *      x       - 中心对应的屏幕位置x
145  *      y       - 中心对应的屏幕位置y
146  *      sc      - 缩放系数
147  * 返回值:
148  *      无
149  */
150     public Polar(double lon, double lat, int x, int y, double sc) {
151         reset(lon, lat, x, y, sc, 0.0);
152     }
153 
154 /**
155  * 功能:构造函数
156  * 参数:
157  *      lon     - 中心对应的经度坐标
158  *      lat     - 中心对应的纬度坐标
159  *      x       - 中心对应的屏幕位置x
160  *      y       - 中心对应的屏幕位置y
161  *      sc      - 缩放系数
162  *      agl     = 仰角
163  * 返回值:
164  *      无
165  */
166     public Polar(double lon, double lat, int x, int y, double sc, double agl) {
167         reset(lon, lat, x, y, sc, agl);
168     }
169 
170 /**
171  * 功能:获得仰角
172  * 参数:
173  *      无
174  * 返回值:
175  *      仰角的度数
176  */
177     public double getElevation() {
178         return(Math.toDegrees(elevation));
179     }
180 
181 /**
182  * 功能:获得经纬度对应的屏幕像素坐标,与雷达仰角有关,主要用于体扫数据显示、底图叠加等。
183  * 参数:
184  *      lon - 经度
185  *      lat - 纬度
186  * 返回值:
187  *      对应的屏幕坐标
188  */
189     public Point getPosition(double lon, double lat) {
190         double  disX    = distanceOfSphere(lon, center.y, center.x, center.y)/cosineElevation;
191         double  disY    = distanceOfSphere(center.x, lat, center.x, center.y)/cosineElevation;
192         double  x       =  (lon>center.x?1:-1)*(disX*perKilometer*scale+ place.x + 0.5;
193         double  y       = -(lat>center.y?1:-1)*(disY*perKilometer*scale+ place.y + 0.5;
194         return(new Point((int)x,(int)y));
195     }
196 
197 /**
198  * 功能:获得极坐标对应的屏幕像素坐标,与雷达仰角无关,主要用于体扫数据显示、底图叠加等。
199  * 参数:
200  *      radius      - 极半径
201  *      angle       - 角度(以正北方向顺时针)
202  * 返回值:
203  *      对应的屏幕坐标
204  */
205 
206     public Point getXY(double radius, double angle) {
207         int x   = (int)(0.5 + radius * Math.sin(Math.toRadians(angle)));
208         int y   = (int)(0.5 + radius * Math.cos(Math.toRadians(angle)));
209         return(new Point(place.x+x, place.y-y));
210     }
211 
212 /**
213  * 功能:获得屏幕像素点位置的极坐标半径,由于是输入参数是扫描平面上的值,故与雷达仰角无关。
214  * 参数:
215  *      x   - 水平坐标
216  *      y   - 垂直坐标
217  * 返回值:
218  *      与极坐标中心的距离,即极半径
219  */
220     public double getRadius(int x, int y) {
221         return(Math.sqrt(1.0*(x-place.x)*(x-place.x)+1.0*(y-place.y)*(y-place.y)));
222     }
223 
224 /**
225  * 功能:获得经纬度位置的极坐标半径,与雷达仰角有关。
226  * 参数:
227  *      lon - 经度坐标
228  *      lat - 纬度坐标
229  * 返回值:
230  *      与极坐标中心的距离(象素点),即极半径
231  */
232     public double getRadius(double lon, double lat) {
233         Point   pos = getPosition(lon, lat);//此函数已经考虑了仰角的影响
234         return(getRadius(pos.x, pos.y));
235     }
236 
237 /**
238  * 功能:获得屏幕像素点位置的极坐标角度(扫描平面与0度平面均相同),与雷达仰角无关。
239  * 参数:
240  *      x   - 水平坐标
241  *      y   - 垂直坐标
242  * 返回值:
243  *      角度值,自正北方向顺时针
244  */
245     public double getAngle(int x, int y) {
246         double  agl = 0.0;
247         ifx == place.x && y == place.y ) {//重合
248             agl = 0.0;
249         }
250         else ifx == place.x ) {
251             agl = y > place.y ? 180.0 360.0;
252         }
253         else ify == place.y ) {
254             agl = x > place.x ? 90.0 270.0;
255         }
256         else {
257             agl = Math.toDegrees(Math.atan(1.0*Math.abs(x-place.x)/Math.abs(y-place.y)));
258             agl =
259                     x > place.x && y < place.y ? agl :          //直角坐标的第一象限
260                     x < place.x && y < place.y ? 180.0 - agl :  //直角坐标的第二象限
261                     x < place.x && y > place.y ? 180.0 + agl :  //直角坐标的第三象限
262                     x > place.x && y > place.y ? 360.0 - agl :  //直角坐标的第四象限
263                     agl;
264         }
265         System.out.println(agl);
266         return(agl);
267     }
268 
269 /**
270  * 功能:获得经纬度位置的极坐标角度(扫描平面与0度平面均相同),与雷达仰角无关。
271  * 参数:
272  *      lon - 水平坐标
273  *      lat - 垂直坐标
274  * 返回值:
275  *      角度值,自正北方向顺时针
276  */
277     public double getAngle(double lon, double lat) {
278 /*
279 //若通过获得屏幕坐标来计算角度,精度比较差,特别是在极坐标中心附近
280         Point   p   = getPosition(lon, lat);
281         return(getAngle(p.x, p.y);
282 */
283         double  agl = 0.0;
284         iflon == center.x && lat == center.y ) {//重合
285             agl = 0.0;
286         }
287         else iflon == center.x ) {
288             agl = lat > center.y ? 360.0 180.0;
289         }
290         else iflat == center.y ) {
291             agl = lon > center.x ? 90.0 270.0;
292         }
293         else {
294             //注:由于经向和纬向的球面距离不等(华南,经向>纬向),故点(1,1)与中心点(0,0)的极角不等45度,而应是略大于45度
295             agl = Math.toDegrees(Math.atan((Math.abs(lon-center.x)*kmPerDegreeX)/(Math.abs(lat-center.y)*kmPerDegreeY)));
296             agl =
297                     lon > center.x && lat > center.y ? agl :            //第一象限
298                     lon < center.x && lat > center.y ? 180.0 - agl :    //第二象限
299                     lon < center.x && lat < center.y ? 180.0 + agl :    //第三象限
300                     lon > center.x && lat < center.y ? 360.0 - agl :    //第四象限
301                     agl;
302         }
303         return(agl);
304     }
305 
306 /**
307  * 功能:
308  *      获得屏幕坐标对应的经纬度
309  * 参数:
310  *      x       - 屏幕水平坐标
311  *      y       - 屏幕垂直坐标
312  * 返回值:
313  *      对应的经纬度
314  */
315     public Point2D.Double getCoordinate(int x, int y) {
316         double  lat         = Math.toDegrees(Math.toRadians(center.y(place.y-y)*cosineElevation/perKilometer/scale/Polar.RADIUS);
317         double  disX0       = distanceOfSphere(center.x, lat, center.x+1.0, lat);//0度平面上1经度的球面距离
318         double  disX        = disX0 / cosineElevation;      //扫描平面上1经度的距离
319         double  perDegreeX  = disX * perKilometer * scale;          //扫描平面上1经度的对应的像素点数
320         double  lon         = center.x + (x - place.x/ perDegreeX;
321         return(new Point2D.Double(lon, lat));
322     }
323 
324 /**
325  * 功能:
326  *      画经线、纬线
327  * 参数:
328  *      g       - 图形设备
329  *      f       - 字体
330  *      c       - 画线颜色
331  *      inc_lon - 经线间隔//未使用
332  *      inc_lat - 纬线间隔//未使用
333  * 返回值:
334  *      无
335  */
336     public void drawGridLine(Graphics2D g, Font f, Color c, int inc_lon, int inc_lat) {
337         Color   saveColor   = g.getColor();
338         Font    saveFont    = g.getFont();
339 
340         //以下两行改进线条的锯齿
341         //RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
342         //g.setRenderingHints(renderHints);
343 
344 //      g.setColor(Color.black);//背景色
345 //      g.fillRect(c.x-(int)(z*240), c.y-(int)(z*240), (int)(z*240*2), (int)(z*240*2));
346 
347         g.setColor(c);//雷达图形区域的边框颜色
348         g.drawRect((int)(0.5+place.x-scale*240)(int)(0.5+place.y-scale*240)(int)(0.5+scale*240*2)(int)(0.5+scale*240*2));
349 
350         //画极径
351         Point   pos1, pos2;
352         for(double i=0.0;i<180.0;i=i+30.0) {
353             pos1    = getXY(scale*240.0,   0.0+i);
354             pos2    = getXY(scale*240.0180.0+i);
355             g.drawLine(pos1.x, pos1.y, pos2.x, pos2.y);
356         }
357 
358         //画极圈
359         for(int i=50;i<=200;i=i+50) {//每50公里画一个圈
360             g.drawArc((int)(0.5+place.x-scale*i)(int)(0.5+place.y-scale*i)(int)(0.5+scale*i*2)(int)(0.5+scale*i*2)0360);
361         }
362         g.drawArc((int)(0.5+place.x-scale*240)(int)(0.5+place.y-scale*240)(int)(0.5+scale*240*2)(int)(0.5+scale*240*2)0360);//外圈240公里
363 
364         g.setFont(saveFont);
365         g.setColor(saveColor);
366     }
367 }

你可能感兴趣的:(Java,Micaps,Projection)