Stereogram(极射赤面)投影--主要用于中高纬和极区的天气图

13 篇文章 0 订阅
7 篇文章 0 订阅

Stereogram.java
001  /*
002 
003     极射赤面投影(Stereogram projection)
004 
005        PACKAGE: cma.common.projection
006       FILENAME: Coordinate.java
007       LANGUAGE: Java2 v1.4
008       ORIGINAL: none
009    DESCRIPTION:
010         CREATE: 2007-07-19 09:29:06
011         UPDATE: 2007-07-23
012         AUTHOR: 刘泽军 (BJ0773@gmail.com)
013                 广西气象减灾研究所
014                 Guangxi Institude of Meteorology and Disaster-reducing Research(GIMDR)
015      REFERENCE: http://mathworld.wolfram.com/StereographicProjection.html
016 
017        Compile: javac Coordinate.java Stereogram.java
018 
019  */
020 
021  package  cma.common.projection;
022 
023  import  java.io.*;
024  import  java.awt.*;
025  import  java.awt.geom.*;
026  import  java.util.*;
027  import  java.lang.Math.*;
028  import  java.text.DecimalFormat;
029 
030  import  cma.common.algorithm.Geometry; //矢量的加法、减法、叉乘
031 
032  public class  Stereogram  extends  Coordinate  {
033 
034  /**
035    * 功能:
036    *      重置参数
037    * 参数:
038    *      lon0,lat0   - 标准经纬度,lat0只能为90或-90,即BBQ或NBQ
039    *      lon1,lat1   - 中心经纬度(用于定位,不一定是中心)
040    *      x,y         - 中心经纬度对应的屏幕坐标
041    *      sc          - 缩放系数
042    * 返回值:
043    *      无
044    */
045       private  void  reset ( //Degree
046               double   lon0,    double   lat0,
047               double   lon1,    double   lat1,
048               int      x,   int      y,
049               double   sc ) {
050           type        = lat0 >=  0.0  ? Coordinate.BBQ : Coordinate.NBQ;
051           standard    =  new  Point2D.Double ( Math.IEEEremainder ( Math.abs ( lon0 ) 360.0 ) , lat0>= 0.0 ? 90.0 :- 90.0 ) ;
052           center      =  new  Point2D.Double (
053                               Math.IEEEremainder ( Math.abs ( lon1 ) 360.0 ) ,
054                               type == Coordinate.BBQ ?
055                                   ( lat1 < - 85.0  ? - 85.0  : lat1 >   90.0  ?   90.0  : lat1 :   //北半球可以以北极为定位点
056                                   ( lat1 >   85.0  ?   85.0  : lat1 < - 90.0  ? - 90.0  : lat1 )     //南半球可以以南极为定位点
057                           ) ;
058           place       =  new  Point ( x, y ) ;
059           scale       = sc <=  0.0  1.0  : sc;
060           double   _lon0   = Math.toRadians ( standard.x ) ;
061           double   _lat0   = Math.toRadians ( standard.y ) ;
062           double   _lon1   = Math.toRadians ( lon1 ) ;
063           double   _lat1   = Math.toRadians ( lat1 ) ;
064           //0.04149是与Micaps1.0匹配的参数
065           double   k       = scale *  0.04149  2.0 *Coordinate.RADIUS/ ( 1.0 +Math.sin ( _lat0 ) *Math.sin ( _lat1 ) +Math.cos ( _lat0 ) *Math.cos ( _lat1 ) *Math.cos ( _lon1-_lon0 )) ;
066           double   x0      = k * Math.cos ( _lat1 ) *Math.sin ( _lon1-_lon0 ) ;
067           double   y0      = k *  ( Math.cos ( _lat0 ) *Math.sin ( _lat1 ) -Math.sin ( _lat0 ) *Math.cos ( _lat1 ) *Math.cos ( _lon1-_lon0 )) ;
068           offset          =  new  Point (( int )( 0.5 +place.x-x0 ) ( int )( 0.5 +place.y-y0 )) ;
069       }
070 
071  /**
072    * 功能:
073    *      构造函数
074    * 参数:
075    *      无(使用缺省值)
076    * 返回值:
077    *      无
078    */
079       public  Stereogram () {
080           reset ( 110.0 90.0 109.40 24.35 640 480 1.0 ) ;
081       }
082 
083  /**
084    * 功能:
085    *      构造函数
086    * 参数:
087    *      lon,lat     - 中心经纬度,
088    *      px,py       - 中心经纬度对应的屏幕坐标
089    *      sc          - 缩放系数
090    * 返回值:
091    *      无
092    */
093       public  Stereogram ( double  lon,  double  lat,  int  px,  int  py,  double  sc ) {
094           reset ( 110.0 35.0 , lon, lat, px, py, sc ) ;
095       }
096 
097  /**
098    * 功能:
099    *      构造函数
100    * 参数:
101    *      lon,lat     - 中心经纬度,
102    *      px,py       - 中心经纬度对应的屏幕坐标
103    *      sx,sy       - 缩放比例
104    * 返回值:
105    *      无
106    */
107       public  Stereogram ( double  lon0,  double  lat0,  double  lon1,  double  lat1,  int  px,  int  py,  double  sc ) {
108           reset ( lon0, lat0, lon1, lat1, px, py, sc ) ;
109       }
110 
111  /**
112    * 功能:
113    *      获得屏幕坐标
114    * 参数:
115    *      lon     - 经度
116    *      lat     - 纬度
117    * 返回值:
118    *      屏幕坐标
119    */
120       public  Point getPosition ( double  lon,  double  lat ) {
121  /*
122           double  _lon0   = Math.toRadians(standard.x);
123           double  _lat0   = Math.toRadians(standard.y);
124           double  _lon1   = Math.toRadians(lon);
125           double  _lat1   = Math.toRadians(lat);
126           //0.04149是与Micaps1.0匹配的参数,
127           double  k       = scale * 0.04149 * 2.0*Coordinate.RADIUS/(1.0+Math.sin(_lat0)*Math.sin(_lat1)+Math.cos(_lat0)*Math.cos(_lat1)*Math.cos(_lon1-_lon0));
128           double  x       = k * Math.cos(_lat1)*Math.sin(_lon1-_lon0);
129           double  y       = k * (Math.cos(_lat0)*Math.sin(_lat1)-Math.sin(_lat0)*Math.cos(_lat1)*Math.cos(_lon1-_lon0));
130           return(new Point((int)(0.5+offset.x+x), (int)(0.5+offset.y-y)));
131  */
132           double   deltaLon    = Math.toRadians ( lon-standard.x ) ;
133           double   radianLat   = Math.toRadians ( lat ) ;
134           //sign=sin(+/-90)=+/-1, cos(90)=cos(-90)=0.0,优化,去掉与标准纬度(即两个极点)有关的三角函数
135           double   sgn     = standard.y ==  90.0  1.0  : - 1.0 ;
136           //0.04149是与Micaps1.0匹配的参数,
137           double   k       = scale *  0.04149  2.0 *Coordinate.RADIUS/ ( 1.0 +sgn*Math.sin ( radianLat )) ;
138           double   x       = k * Math.cos ( radianLat ) *Math.sin ( deltaLon ) ;
139           double   y       = k *  ( -sgn*Math.cos ( radianLat ) *Math.cos ( deltaLon )) ;
140           return ( new  Point (( int )( 0.5 +offset.x+x ) ( int )( 0.5 +offset.y-y ))) ;
141       }
142 
143  /**
144    * 功能:
145    *      获得屏幕坐标对应的经纬度
146    * 参数:
147    *      x       - 屏幕水平坐标
148    *      y       - 屏幕垂直坐标
149    * 返回值:
150    *      对应的经纬度
151    */
152       public  Point2D.Double getCoordinate ( int  x,  int  y ) {
153           Point   p0      =  this .getPosition ( 0.0 , standard.y ) ;
154           if p0.x == x && p0.y == y  ) {
155               return ( new  Point2D.Double ( 0.0 , standard.y )) ;
156           }
157           Point   p1      =  this .getPosition ( 0.0 0.0 ) ;
158           if p1.x == x && p1.y == y  ) {
159               return ( new  Point2D.Double ( 0.0 0.0 )) ;
160           }
161           //求未知点与极点、(0.0E,0.0N)的夹角
162           double   x1      = p0.x;
163           double   y1      = p0.y;
164           double   x2      = p1.x;
165           double   y2      = p1.y;
166           double   x3      = x;
167           double   y3      = y;
168           //增加临时变量,但节约了计算量,由加减法17次,乘除法12次,调用函数5次,优化为加减法11次,乘除法8次,调用函数4次
169           double   x3_x2   = x3-x2;
170           double   y3_y2   = y3-y2;
171           double   aa      = x3_x2*x3_x2+y3_y2*y3_y2;
172           double   x3_x1   = x3-x1;
173           double   y3_y1   = y3-y1;
174           double   bb      = x3_x1*x3_x1+y3_y1*y3_y1;
175           double   b       = Math.sqrt ( bb ) ;
176           double   x1_x2   = x1-x2;
177           double   y1_y2   = y1-y2;
178           double   cc      = x1_x2*x1_x2+y1_y2*y1_y2;
179           double   c       = Math.sqrt ( cc ) ;
180           double   angle   = Math.acos (( bb+cc-aa ) / ( 2 *b*c )) ; //余弦定理,值在0-180之间
181           double   pos     = Geometry.pos ( p1.x, p1.y, p0.x, p0.y, x, y ) ; //判断点相对于0度经线的位置
182           if pos >  0.0  ) { //位于180-360度区域
183               angle   =  2.0  * Math.PI - angle;
184           }
185           double   lon     = Math.toDegrees ( angle ) ;
186 
187           double   deltaLon    = Math.toRadians ( lon-standard.x ) ;
188           double   sgn         = standard.y ==  90.0  1.0  : - 1.0 ;
189           double   k           =  ( offset.y - y ( -sgn * scale *  0.04149  2.0  * Coordinate.RADIUS * Math.cos ( deltaLon )) ;
190           double   lat         = Math.toDegrees ( Math.asin (( 1 -sgn*k*k ) / ( 1 +k*k ))) ; //由getPosition方法的公式反推得到
191 
192           return ( new  Point2D.Double ( lon, lat )) ;
193       }
194 
195  /**
196    * 功能:获得相对于标准经度的偏角(逆时针方向)
197    * 参数:
198    *  degreeLon   - 经度值
199    *  degreeLat   - 纬度值
200    * 返回值:
201    *      偏角
202    */
203       public  double  getAngle ( double  degreeLon,  double  degreeLat ) {
204           double   agl =  360.0 +standard.x-degreeLon;
205           while ( agl>= 360.0 ) {
206               agl -=  360.0 ;
207           }
208           while ( agl<=- 360.0 ) {
209               agl +=  360.0 ;
210           }
211           return ( agl ) ;
212       }
213 
214  /**
215    * 功能:
216    *      画经线、纬线
217    * 参数:
218    *      g       - 图形设备
219    *      f       - 字体
220    *      c       - 画线颜色
221    *      inc_lon - 经线间隔
222    *      inc_lat - 纬线间隔
223    * 返回值:
224    *      无
225    */
226       public  void  drawGridLine ( Graphics2D g, Font f, Color c,  int  inc_lon,  int  inc_lat ) {
227 
228           DecimalFormat   df  =  new  DecimalFormat ( "0.##########" ) ;
229           String  lonString   = df.format ( inc_lon ) ;
230           String  latString   = df.format ( inc_lat ) ;
231           DecimalFormat   dfLon   =  new  DecimalFormat ( "0." + ( "##########" .substring ( 0 , lonString.indexOf ( "." ) > 0 ?lonString.length () -lonString.indexOf ( "." ) : 1 ))) ;
232           DecimalFormat   dfLat   =  new  DecimalFormat ( "0." + ( "##########" .substring ( 0 , latString.indexOf ( "." ) > 0 ?latString.length () -latString.indexOf ( "." ) : 1 ))) ;
233 
234           Color   saveColor   = g.getColor () ;
235           Font    saveFont    = g.getFont () ;
236           g.setColor ( c ) ;
237           g.setFont ( null ==f?f: new  Font ( "Times New Roman" , Font.PLAIN,  12 )) ;
238           FontMetrics fm  = g.getFontMetrics () ;
239           String      text;
240           byte         tmpByte [] ;
241           int          bytesWidth, bytesHeight = fm.getHeight () ;
242           Point       pos0, pos1, pos2;
243           pos0        = getPosition ( standard.x, type==Coordinate.BBQ? 90.0 :- 90.0 ) ;
244           double       rotateAngle;
245           if inc_lon >  ) { //经线间隔为正值
246               for ( double  lon= 0.0 ;lon< 360.0 ;lon=lon+inc_lon ) { //画经线
247                   pos1    = getPosition ( lon, 0.0 ) ; //在赤道标经度值
248                   g.translate ( pos1.x, pos1.y ) ;
249                   rotateAngle =  this .getAngle ( lon,  0.0 ) ;
250                   g.rotate ( Math.toRadians ( rotateAngle )) ;   //偏角
251                   text        = dfLon.format ( lon ) ;
252                   tmpByte     = text.getBytes () ;
253                   bytesWidth  = fm.bytesWidth ( tmpByte,  0 , tmpByte.length ) ;
254                   g.drawString ( text, -bytesWidth/ 2 , bytesHeight/ 3 ) ;
255                   g.rotate ( Math.toRadians ( -rotateAngle )) //偏角
256                   g.translate ( -pos1.x, -pos1.y ) ;
257 
258                   pos1    = getPosition ( lon,- 90.0 + ( inc_lat> 0.0 ?inc_lat: 10.0 )) ;
259                   pos2    = getPosition ( lon,  90.0 - ( inc_lat> 0.0 ?inc_lat: 10.0 )) ;
260                   g.drawLine ( pos1.x, pos1.y, pos2.x, pos2.y ) ; //画经线
261               }
262           }
263           if inc_lat >  ) { //纬线间隔为正值
264               int  r   =  0 ;
265               for ( double  lat= 90.0 -inc_lat;lat>=- 90.0 +inc_lat;lat=lat-inc_lat ) { //画纬线(纬圈)
266                   pos1    = getPosition ( standard.x, lat ) ;
267                   r       = Math.abs ( pos1.y-pos0.y ) ;
268                   g.drawArc ( pos0.x-r, pos0.y-r, 2 *r, 2 *r, 0 , 360 ) ;
269               }
270               for ( double  lon= 0.0 ;lon< 360.0 ;lon=lon+ 90.0 ) { //在0、90、180、270经线上标纬度值
271                   for ( double  lat= 90.0 -inc_lat;lat>=- 90.0 +inc_lat;lat=lat-inc_lat ) {
272                       pos1    = getPosition ( lon,lat ) ;
273                       g.translate ( pos1.x, pos1.y ) ;
274                       rotateAngle =  this .getAngle ( lon, lat ) ;
275                       g.rotate ( Math.toRadians ( rotateAngle )) ;   //偏角
276                       text        = dfLat.format ( lat ) ;
277                       tmpByte     = text.getBytes () ;
278                       bytesWidth  = fm.bytesWidth ( tmpByte,  0 , tmpByte.length ) ;
279                       g.drawString ( text, -bytesWidth/ 2 , bytesHeight/ 3 ) ;
280  //                  g.drawString(text, pos1.x-bytesWidth/2, pos1.y+bytesHeight/3);
281                       g.rotate ( Math.toRadians ( -rotateAngle )) //偏角
282                       g.translate ( -pos1.x, -pos1.y ) ;
283                   }
284               }
285           }
286           g.setFont ( saveFont ) ;
287           g.setColor ( saveColor ) ;
288       }
289  }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Pascal 3D标注工具是一款用于三维物体检测和标注的工具。它是基于Pascal VOC数据集的扩展,并在其增加了对三维物体的标注能力。 首先,Pascal 3D标注工具提供了一个直观易用的用户界面,使得用户能够方便地加载和查看三维物体的图像和模型数据。用户可以通过鼠标和键盘进行交互,标注和测量三维物体的位置、姿态和尺寸。 其次,Pascal 3D标注工具支持多种标注方式和工具,以满足不同的标注需求。用户可以使用包围盒(Bounding Box)标注三维物体的位置和尺寸;使用线框(Wireframe)标注物体的轮廓和边缘;使用关键(Keypoint)标注物体的特征或关键;使用立体文本(Stereogram)标注物体的深度和立体形状。 此外,Pascal 3D标注工具还提供了自动化的标注功能,可以根据已有的模型和算法自动检测和标注三维物体。用户可以根据需要选择手动标注或自动标注的方式,从而快速而准确地完成标注任务。 最后,Pascal 3D标注工具还支持标注数据的导出和共享。用户可以将标注结果导出为各种常见的数据格式,如XML、JSON或CSV,以便在其他应用程序或流程使用。同时,用户还可以将标注数据共享给其他团队成员,以便合作和共同完成标注工作。 总结来说,Pascal 3D标注工具是一款强大而灵活的三维物体标注工具,具有直观易用的界面、多种标注方式和工具、自动化的标注功能以及导出和共享标注数据的能力。它能够帮助研究人员和工程师快速、准确地完成三维物体的标注任务。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值