传统的气象预测立足于气压梯度力的推动,相应的信息序是气压、温度、湿度和风,并以气压系统为核心。但实际应用中,所得到的气压(或高度场)的数值是经过静力订正的,且没有大气密度的观测,使得可使用的气压信息成为了滞后信息。为此,实际重要天气往往发生于气压系统之前,降水后才见到气压系统,而致转折性天气预测屡屡失误。为此,Blowup的V-3θ预测方法中,改变了传统的信息序,即风向、风速、湿度、温度和气压,并以风向为核心。——《走进非规则》,欧阳首承、D.H.McNeil、林益著。
附图:
(蒋MM写的使用方法)
V-3θ图是成都气象学院欧阳首承教授设计出来的运用图像结构来预测天气的结构预测方法,主要是根据大气中压、温、湿、风的垂直分布,判断大气滚流对天气演变的影响来预报天气转折性变化。V-3θ中的3θ指的是θ(位温)、θse(假相当位温)、θ*(假定为饱和状态下的计算值),在图中θ线位于左边,θse居中,θ*居右;而V则是探空资料中各层风向、风速的实际观测值,在图中标在θ*线上。
滚流(即垂直方向上的涡旋流)的判断:
- 潜在的滚流效应:
若三条θ曲线左倾,则为非均匀结构,潜在顺时针滚流,若三条曲线右倾,潜在无滚流或逆时针滚流。 - 现有流场的滚流效应:
在V-3θ图上,风场在中、下层为偏南风(或东风),上层为偏北风(或西风),为顺时针滚流,有利于降水,天气将发生由好向坏的转折性变化。而风场在中、下层为偏北风(或西风),上层为偏南风(或东风),则为逆时针滚流,对降水不利,是坏天气向好天气转换的特征。
强对流天气和暴雨天气在V-3θ图上的区别:
- 强对流天气的主要特征:
(1) 图中低层的θ*线与T轴成钝角,是强对流不同于暴雨的特征;
(2) θ曲线在200hPa附近成陡然左倾或平行于P轴成拐角状态分布,表示对流层顶有超低温现象,也是强对流天气的一个特征。
(3) 较强的强对流的“罗锅”(罗锅指θ线向左弯曲的现象)可出现3~4个,并且低层(850hPa)以下的θ*可达到360°K以上;
(4) 大气层中层偏湿,上、下偏干;或上、下偏湿,中层偏干,即θse和θ*线形成“蜂腰”结构;
(5) 有较强的上、下层温差(40~50℃)。 - 暴雨天气的主要特征:
(1) 一般暴雨基本上在对流层顶附近没有超低温现象,θ曲线原则上呈整体性向左弯曲,或中、低空呈多曲折式向左弯曲;
(2) θse、θ*曲线以θ*-θse≤10~8℃的准平行的形式,垂直于T轴;
(3) 风场的明显特征是大气的滚流中心下边为暖湿气流,中心的上边则为干、冷气流;
(4) 暖湿气流至少在500hPa的高度,特大暴雨时要达到400hPa;
(5) 降水量的等级参照预报站点东侧反气旋流场的高度。
V3Theta.java
001 /**
002
003 处理溃变理论(成气院欧阳首承教授创立)的V-3θ资料
004
005 PACKAGE: cma.cuit.blowup
006 FILENAME: V3Theta.java
007 LANGUAGE: Java2 v1.4
008 ORIGINAL: c++ (CDiamondV3Theta.cpp)
009 DESCRIPTION:
010 CREATE: 2003-08-20
011 UPDATE: 2006-10-02 01:31:23 根据桂林局蒋MM的要求改写为 Java 版
012 AUTHOR: 刘泽军 (BJ0773@gmail.com)
013 DEPARTMENT: 广西柳州市气象台/广西气象减灾研究所
014
015 */
016
017 /*
018 用法:
019 String yyyymmddhh = "2005072800";//UTC
020 int width=640,height=480;
021 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
022 Graphics2D g = image.createGraphics();
023
024 //以下两行改进线条的锯齿
025 RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
026 g.setRenderingHints(renderHints);
027
028 //数据来源自TlogP文件(包含特性层)
029 String filename = "/path/to/micaps/high/Tlogp/" + yyyymmddhh.substring(2,8) + ("00".equals(yyyymmddhh.substring(8,10))?"08":"20") + ".000";
030 V3Theta v3t = new V3Theta(filename);
031
032 //数据来源自Micaps探空A报文处理结果(不包含特性层),可先调用cma.gmb.gmiss.GAKO来处理探空报文
033 // String filename = "/path/to/micaps/datatran/gg" + yyyymmddhh.substring(6,10) + ".dat";
034 // V3Theta v3t = new V3Theta(filename, yyyymmddhh);
035
036 //设置
037 v3t.HEIGHT = false; //填高度值
038 v3t.TEMPER = false; //填温度值
039 v3t.DEWPT = false; //填露点温度值
040 v3t.LOGP = true; //对数压力坐标
041 v3t.WIND = true; //填风向风速
042 v3t.BACKGROUND = false; //填背景网格
043 v3t.SPECIAL = false; //特性层
044
045 //填图
046 v3t.plot(g, 40, 40, "59431");
047
048 g.dispose();
049
050 */
051
052 package cma.cuit.blowup;
053
054 import java.io.*;
055 import java.util.*;
056 import java.awt.*;
057 import java.lang.Math.*;
058 import java.text.DecimalFormat;
059
060 import cma.common.atmos.*;
061 import cma.micaps.diamond.datatype.*;
062 import cma.micaps.diamond.*;
063
064 public class V3Theta {
065
066 public static double VALUE = Formula.VALUE;
067 public double value = VALUE;
068
069 public Vector Data; //数据
070 public Vector Stations; //区站号列表
071 public int Year, Month, Day, Hour, Validate;
072 public boolean Enabled;
073
074 public boolean BACKGROUND = false ; //背景网格
075
076 public boolean LOGP = false ; //对数坐标
077 public boolean SPECIAL = false ; //显示特性层(TlogP文件包含有特性层,DiamondGG文件不包含)
078
079 public boolean WIND = true ; //填风向风速
080 public boolean HEIGHT = false ; //填高度值
081 public boolean TEMPER = false ; //填温度值
082 public boolean DEWPT = false ; //填露点温度值
083
084 public static int xZoom = 4 ;
085 public static int yZoom = 360 ;
086
087 /**
088 * 功能:构造函数
089 * 参数:
090 * filename - DiamondGG 文件名
091 * yyyymmddhh - DiamondGG 文件对应时次
092 * 返回值:
093 * 无
094 */
095 public V3Theta ( String filename, String yyyymmddhh ) {
096 Data = new Vector () ;
097 Stations = new Vector () ;
098 Enabled = loadFromFile ( filename, yyyymmddhh ) ;
099 }
100
101 /**
102 * 功能:构造函数
103 * 参数:
104 * filename - DiamondGG 文件名
105 * yyyymmddhh - DiamondGG 文件对应时次
106 * 返回值:
107 * 无
108 */
109 public V3Theta ( String filename ) {
110 Data = new Vector () ;
111 Stations = new Vector () ;
112 Enabled = loadFromFile ( filename ) ;
113 }
114
115 /**
116 * 功能:加载数据(读取探空资料解码结果文件DiamondGG并计算V3Theta数据)
117 * 参数:
118 * filename - DiamondGG 文件名
119 * yyyymmddhh - DiamondGG 文件对应时次
120 * 返回值:
121 * 无
122 */
123 public boolean loadFromFile ( String filename, String yyyymmddhh ) {
124 try {
125 DiamondGG gg = new DiamondGG ( filename, yyyymmddhh ) ;
126 if ( !gg.Enabled ) {
127 return ( false ) ;
128 }
129 Data.clear () ;
130 Stations.clear () ;
131 Year = gg.Year;
132 Month = gg.Month;
133 Day = gg.Day;
134 Hour = gg.Hour;
135 Validate = 0 ;
136
137 for ( int i= 0 ;i<gg.Data.size () ;i++ ) {
138 V3ThetaData data = new V3ThetaData () ;
139 DiamondDataGG temp = ( DiamondDataGG ) gg.Data.get ( i ) ;
140 data.Station = temp.Station;
141 data.Longitude = temp.Longitude;
142 data.Latitude = temp.Latitude;
143 data.Layers = 12 ; //探空资料解码结果仅有12层
144 for ( int j= 0 ;j<data.Layers;j++ ) {
145 data.Item [ j ] .reset (
146 temp.Item [ j ] .Layer,
147 temp.Item [ j ] .H,
148 temp.Item [ j ] .T,
149 temp.Item [ j ] .T_Td,
150 temp.Item [ j ] .dd,
151 temp.Item [ j ] .ff
152 ) ;
153 if ( temp.Item [ j ] .Layer != 600.0 ) {
154 data.Item [ j ] .proc () ;
155 }
156 }
157 Data.add ( data ) ;
158 Stations.add ( data.Station ) ;
159 }
160 return ( Data.size () > 0 ) ;
161 }
162 catch ( Exception ex ) {
163 System.out.println ( ex.getMessage ()) ;
164 ex.printStackTrace () ;
165 return ( false ) ;
166 }
167 }
168
169 /**
170 * 功能:加载数据(读取探空资料解码后生成的TlogP文件Diamond05并计算V3Theta数据)
171 * 参数:
172 * filename - TlogP 文件名
173 * 返回值:
174 * 无
175 */
176 public boolean loadFromFile ( String filename ) {
177 if ( ! ( new File ( filename )) .exists () ) {
178 return ( false ) ;
179 }
180 try {
181 Data.clear () ;
182 Stations.clear () ;
183 DecimalFormat df = new DecimalFormat ( "0" ) ;
184 InputStreamReader isReader = new InputStreamReader ( new FileInputStream ( filename ) , "gb2312" ) ; //支持汉字
185 BufferedReader bufReader = new BufferedReader ( isReader ) ;
186
187 String lineString;
188 StringTokenizer st;
189 Vector vectorData = new Vector () ;
190 while ( null != ( lineString = bufReader.readLine () ) ) {
191 st = new StringTokenizer ( lineString, " /r/n" ) ;
192 while ( st.hasMoreTokens () ) {
193 vectorData.add ( st.nextToken ()) ;
194 }
195 }
196 isReader.close () ;
197 if ( vectorData.size () < 8 ||
198 ! "diamond" .equalsIgnoreCase (( String ) vectorData.get ( 0 )) || //不是Micaps数据文件
199 5 != Integer.parseInt (( String ) vectorData.get ( 1 )) ) { //不是TlogP数据文件
200 return ( false ) ;
201 }
202
203 Year = Integer.parseInt (( String ) vectorData.get ( 3 )) ;
204 Month = Integer.parseInt (( String ) vectorData.get ( 4 )) ;
205 Day = Integer.parseInt (( String ) vectorData.get ( 5 )) ;
206 Hour = Integer.parseInt (( String ) vectorData.get ( 6 )) ;
207 int count = Integer.parseInt (( String ) vectorData.get ( 7 )) ;
208 Validate = 0 ;
209 int index = 8 ;
210 for ( int i= 0 ;i<count;i++ ) {
211 if ( vectorData.size () - index > 5 && vectorData.size () - index >= Integer.parseInt (( String ) vectorData.get ( index+ 4 )) ) { //存在某站点数据且数据完整
212 V3ThetaData data = new V3ThetaData () ;
213 data.Station = ( String ) vectorData.get ( index++ ) ; //0
214 data.Longitude = Double.parseDouble (( String ) vectorData.get ( index++ )) ; //1
215 data.Latitude = Double.parseDouble (( String ) vectorData.get ( index++ )) ; //2
216 data.Altitude = Double.parseDouble (( String ) vectorData.get ( index++ )) ; //3
217 data.Layers = Integer.parseInt (( String ) vectorData.get ( index++ )) / 6 ; //4
218 // System.out.println(data.Station + " " + data.Longitude + " " + data.Latitude + " " + data.Altitude + " " + data.Layers);
219 for ( int j= 0 ;j<data.Layers;j++ ) { //每层6个数据
220 data.Item [ j%V3ThetaData.LAYERS ] .layer = Integer.parseInt (( String ) vectorData.get ( index++ )) ; //0
221 data.Item [ j%V3ThetaData.LAYERS ] .h = Double.parseDouble (( String ) vectorData.get ( index++ )) ; //1
222 data.Item [ j%V3ThetaData.LAYERS ] .t = Double.parseDouble (( String ) vectorData.get ( index++ )) ; //2
223 data.Item [ j%V3ThetaData.LAYERS ] .t_td = Double.parseDouble (( String ) vectorData.get ( index++ )) ; //3
224 data.Item [ j%V3ThetaData.LAYERS ] .dd = Integer.parseInt (( String ) vectorData.get ( index++ )) ; //4
225 data.Item [ j%V3ThetaData.LAYERS ] .ff = Integer.parseInt (( String ) vectorData.get ( index++ )) ; //5
226 if ( data.Item [ j%V3ThetaData.LAYERS ] .t != value && data.Item [ j%V3ThetaData.LAYERS ] .t_td != value ) {
227 data.Item [ j%V3ThetaData.LAYERS ] .t_td = data.Item [ j%V3ThetaData.LAYERS ] .t - data.Item [ j%V3ThetaData.LAYERS ] .t_td;
228 }
229 else {
230 data.Item [ j%V3ThetaData.LAYERS ] .t_td = value;
231 }
232 data.Item [ j%V3ThetaData.LAYERS ] .proc () ;
233 Data.add ( data ) ;
234 Stations.add ( data.Station ) ;
235 }
236 }
237 }
238 return ( Data.size () > 0 ) ;
239 }
240 catch ( Exception ex ) {
241 System.out.println ( ex.getMessage ()) ;
242 ex.printStackTrace () ;
243 return ( false ) ;
244 }
245 }
246
247 /**
248 * 功能:计算V3Theta的最大值
249 * 参数:
250 * data - 站点的V-3Theta数据
251 * 返回值:
252 * 该站点所有层次的Theta、Thetase、Theta*的最大值
253 */
254 public double maxTheta ( V3ThetaData data ) {
255 DecimalFormat df = new DecimalFormat ( "0" ) ;
256 double qMax = 390.0 ;
257 for ( int i= 0 ;i<data.Layers;i++ ) {
258 if ( data.Item [ i ] .q1 != value && data.Item [ i ] .q1 > qMax ) {
259 qMax = data.Item [ i ] .q1;
260 }
261 if ( data.Item [ i ] .q2 != value && data.Item [ i ] .q2 > qMax ) {
262 qMax = data.Item [ i ] .q2;
263 }
264 if ( data.Item [ i ] .q3 != value && data.Item [ i ] .q3 > qMax ) {
265 qMax = data.Item [ i ] .q3;
266 }
267 }
268 return ( qMax ) ;
269 }
270
271 /**
272 * 功能:计算V3Theta的最小值
273 * 参数:
274 * data - 站点的V-3Theta数据
275 * 返回值:
276 * 该站点所有层次的Theta、Thetase、Theta*的最小值
277 */
278 public double minTheta ( V3ThetaData data ) {
279 DecimalFormat df = new DecimalFormat ( "0" ) ;
280 double qMin = 290.0 ;
281 for ( int i= 0 ;i<data.Layers;i++ ) {
282 if ( data.Item [ i ] .q1 != value && data.Item [ i ] .q1 < qMin ) {
283 qMin = data.Item [ i ] .q1;
284 }
285 if ( data.Item [ i ] .q2 != value && data.Item [ i ] .q2 < qMin ) {
286 qMin = data.Item [ i ] .q2;
287 }
288 if ( data.Item [ i ] .q3 != value && data.Item [ i ] .q3 < qMin ) {
289 qMin = data.Item [ i ] .q3;
290 }
291 }
292 return ( qMin ) ;
293 }
294
295 /**
296 * 功能:获得指定站号的V-3Theta数据
297 * 参数:
298 * data - 返回的数据克隆(对返回的data进行修改并不影响原始数据)
299 * station - 探空站的站号
300 * 返回值:
301 * 存在该数据并复制成功则返回true,否则返回false。
302 */
303 public boolean get ( V3ThetaData data, String station ) {
304 if ( !Enabled || Data.size () != Stations.size () ||
305 null == station || "" .equals ( station ) ) {
306 return ( false ) ;
307 }
308 try {
309 int index = Stations.indexOf ( station ) ;
310 if ( index == - 1 ) {
311 return ( false ) ;
312 }
313 V3ThetaData tmp = ( V3ThetaData ) Data.get ( index ) ;
314 if ( !station.equalsIgnoreCase ( tmp.Station ) ) {
315 return ( false ) ;
316 }
317 data.Station = tmp.Station;
318 data.Longitude = tmp.Longitude;
319 data.Latitude = tmp.Latitude;
320 data.Altitude = tmp.Altitude;
321 data.Layers = tmp.Layers;
322 for ( int j= 0 ;j<data.Item.length;j++ ) {
323 data.Item [ j ] .layer = tmp.Item [ j ] .layer;
324 data.Item [ j ] .h = tmp.Item [ j ] .h;
325 data.Item [ j ] .t = tmp.Item [ j ] .t;
326 data.Item [ j ] .t_td = tmp.Item [ j ] .t_td;
327 data.Item [ j ] .dd = tmp.Item [ j ] .dd;
328 data.Item [ j ] .ff = tmp.Item [ j ] .ff;
329 data.Item [ j ] .q1 = tmp.Item [ j ] .q1;
330 data.Item [ j ] .q2 = tmp.Item [ j ] .q2;
331 data.Item [ j ] .q3 = tmp.Item [ j ] .q3;
332 }
333 return ( true ) ;
334 }
335 catch ( Exception ex ) {
336 System.out.println ( ex.getMessage ()) ;
337 ex.printStackTrace () ;
338 return ( false ) ;
339 }
340 }
341
342 /**
343 * 功能:V3Theta填图
344 * 参数:
345 * filename - DiamondGG 文件名
346 * yyyymmddhh - DiamondGG 文件对应时次
347 * 返回值:
348 * 无
349 */
350 public boolean plot ( Graphics2D g, int left, int top, String station ) {
351 if ( !Enabled ) {
352 return ( false ) ;
353 }
354 V3ThetaData data = new V3ThetaData () ;
355 if ( !get ( data, station ) ) {
356 return ( false ) ;
357 }
358 DecimalFormat df = new DecimalFormat ( "0.#" ) ;
359 DecimalFormat dfInt = new DecimalFormat ( "0" ) ;
360 int x [][] = {{ 0 , 0 } , { 0 , 0 } , { 0 , 0 }} , y [][] = {{ 0 , 0 } , { 0 , 0 } , { 0 , 0 }} ;
361 boolean p [] = { false,false,false } ;
362 boolean pp [] = { false,false,false } ;
363 double minLayerValue = 100 ;
364 double maxLayerValue = 1032 ;
365 double LayerIndex [] = { 100 , 150 , 200 , 250 , 300 , 400 , 500 , 600 , 700 , 850 , 925 , 1000 } ; //固定层次
366 Vector layers = new Vector () ;
367 for ( int i= 0 ;i< 12 ;i++ ) {
368 layers.add ( df.format ( LayerIndex [ i ])) ;
369 }
370
371 FontMetrics fm = g.getFontMetrics () ;
372
373 double qMin = minTheta ( data ) ;
374 double qMax = maxTheta ( data ) ;
375 background ( g, left, top, data ) ; //画背景图
376 for ( int j= 0 ;j<data.Layers;j++ )
377 if ( SPECIAL || //显示所有层次
378 layers.indexOf ( df.format ( data.Item [ j ] .layer )) != - 1 ) { //是固定层次
379 pp [ 0 ] = false ;
380 pp [ 1 ] = false ;
381 pp [ 2 ] = false ;
382 if ( data.Item [ j ] .layer != 0 && data.Item [ j ] .layer != 9999 && data.Item [ j ] .q1 != 9999 ) {
383 if ( LOGP ) {
384 y [ 0 ][ 1 ] = Integer.parseInt ( dfInt.format ( top + ( Math.log ( data.Item [ j ] .layer ) -Math.log ( minLayerValue )) / ( Math.log ( maxLayerValue ) -Math.log ( minLayerValue )) *yZoom )) ;
385 }
386 else {
387 y [ 0 ][ 1 ] = Integer.parseInt ( dfInt.format ( top + ( data.Item [ j ] .layer-minLayerValue ) / ( maxLayerValue-minLayerValue ) *yZoom )) ;
388 }
389 x [ 0 ][ 1 ] = Integer.parseInt ( dfInt.format ( left + ( data.Item [ j ] .q1 - qMin ) * xZoom )) ;
390 if ( p [ 0 ] == false ) {
391 p [ 0 ] = true ;
392 }
393 else {
394 g.setColor ( Color.green ) ;
395 g.drawLine ( x [ 0 ][ 0 ] , y [ 0 ][ 0 ] , x [ 0 ][ 1 ] , y [ 0 ][ 1 ]) ;
396 g.fillArc ( x [ 0 ][ 1 ] , y [ 0 ][ 1 ] , 2 , 2 , 0 , 360 ) ;
397 }
398 x [ 0 ][ 0 ] = x [ 0 ][ 1 ] ;
399 y [ 0 ][ 0 ] = y [ 0 ][ 1 ] ;
400 pp [ 0 ] = true ;
401 }
402
403 if ( data.Item [ j ] .layer != 0 && data.Item [ j ] .layer != 9999 && data.Item [ j ] .q2 != 9999 ) {
404 if ( LOGP ) {
405 y [ 1 ][ 1 ] = Integer.parseInt ( dfInt.format ( top + ( Math.log ( data.Item [ j ] .layer ) -Math.log ( minLayerValue )) / ( Math.log ( maxLayerValue ) -Math.log ( minLayerValue )) *yZoom )) ;
406 }
407 else {
408 y [ 1 ][ 1 ] = Integer.parseInt ( dfInt.format ( top + ( data.Item [ j ] .layer-minLayerValue ) / ( maxLayerValue-minLayerValue ) *yZoom )) ;
409 }
410 x [ 1 ][ 1 ] = Integer.parseInt ( dfInt.format ( left + ( data.Item [ j ] .q2 - qMin ) * xZoom )) ;
411 if ( p [ 1 ] == false ) {
412 p [ 1 ] = true ;
413 }
414 else {
415 g.setColor ( Color.red ) ;
416 g.drawLine ( x [ 1 ][ 0 ] , y [ 1 ][ 0 ] , x [ 1 ][ 1 ] , y [ 1 ][ 1 ]) ;
417 g.fillArc ( x [ 1 ][ 1 ] , y [ 1 ][ 1 ] , 2 , 2 , 0 , 360 ) ;
418 }
419 x [ 1 ][ 0 ] = x [ 1 ][ 1 ] ;
420 y [ 1 ][ 0 ] = y [ 1 ][ 1 ] ;
421 pp [ 1 ] = true ;
422 }
423
424 if ( data.Item [ j ] .layer != 0 && data.Item [ j ] .layer != 9999 && data.Item [ j ] .q3 != 9999 ) {
425 if ( LOGP ) {
426 y [ 2 ][ 1 ] = Integer.parseInt ( dfInt.format ( top + ( Math.log ( data.Item [ j ] .layer ) -Math.log ( minLayerValue )) / ( Math.log ( maxLayerValue ) -Math.log ( minLayerValue )) *yZoom )) ;
427 }
428 else {
429 y [ 2 ][ 1 ] = Integer.parseInt ( dfInt.format ( top + ( data.Item [ j ] .layer-minLayerValue ) / ( maxLayerValue-minLayerValue ) *yZoom )) ;
430 }
431 x [ 2 ][ 1 ] = Integer.parseInt ( dfInt.format ( left + ( data.Item [ j ] .q3 - qMin ) * xZoom )) ;
432 if ( p [ 2 ] == false ) {
433 p [ 2 ] = true ;
434 }
435 else {
436 g.setColor ( Color.blue ) ;
437 g.drawLine ( x [ 2 ][ 0 ] , y [ 2 ][ 0 ] , x [ 2 ][ 1 ] , y [ 2 ][ 1 ]) ;
438 g.fillArc ( x [ 2 ][ 1 ] , y [ 2 ][ 1 ] , 2 , 2 , 0 , 360 ) ;
439 }
440 x [ 2 ][ 0 ] = x [ 2 ][ 1 ] ;
441 y [ 2 ][ 0 ] = y [ 2 ][ 1 ] ;
442 pp [ 2 ] = true ;
443 }
444
445 if ( pp [ 2 ] || pp [ 1 ] || pp [ 0 ] ) {
446 if ( WIND && data.Item [ j ] .dd != value && data.Item [ j ] .ff != value ) {
447 g.setColor ( Color.black ) ;
448 Meteorology.plotWind (
449 g,
450 new Point ( pp [ 2 ] ? x [ 2 ][ 1 ] : pp [ 1 ] ? x [ 1 ][ 1 ] : x [ 0 ][ 1 ] , pp [ 2 ] ? y [ 2 ][ 1 ] : pp [ 1 ] ? y [ 1 ][ 1 ] : y [ 0 ][ 1 ]) ,
451 data.Latitude > 0 ,
452 0 ,
453 data.Item [ j ] .dd,
454 data.Item [ j ] .ff ) ;
455 }
456 if ( HEIGHT && data.Item [ j ] .h != value ) {
457 g.setColor ( Color.blue ) ;
458 g.drawString (
459 df.format ( data.Item [ j ] .h ) ,
460 ( pp [ 2 ] ? x [ 2 ][ 1 ] : pp [ 1 ] ? x [ 1 ][ 1 ] : x [ 0 ][ 1 ]) + 8 ,
461 ( pp [ 2 ] ? y [ 2 ][ 1 ] : pp [ 1 ] ? y [ 1 ][ 1 ] : y [ 0 ][ 1 ]) + fm.getHeight () / 4
462 ) ;
463 }
464
465 if ( TEMPER && data.Item [ j ] .t != value ) {
466 g.setColor ( Color.red ) ;
467 g.drawString (
468 df.format ( data.Item [ j ] .t ) ,
469 ( pp [ 2 ] ? x [ 2 ][ 1 ] : pp [ 1 ] ? x [ 1 ][ 1 ] : x [ 0 ][ 1 ]) + 8 +
470 ( HEIGHT ? fm.bytesWidth ( df.format ( data.Item [ j ] .h ) .getBytes () , 0 , df.format ( data.Item [ j ] .h ) .getBytes () .length ) + 8 : 0 ) ,
471 ( pp [ 2 ] ? y [ 2 ][ 1 ] : pp [ 1 ] ? y [ 1 ][ 1 ] : y [ 0 ][ 1 ]) + fm.getHeight () / 4
472 ) ;
473 }
474
475 if ( DEWPT && data.Item [ j ] .t != value && data.Item [ j ] .t_td != value ) {
476 g.setColor ( Color.darkGray ) ;
477 g.drawString (
478 df.format ( data.Item [ j ] .t - data.Item [ j ] .t_td ) ,
479 ( pp [ 2 ] ? x [ 2 ][ 1 ] : pp [ 1 ] ? x [ 1 ][ 1 ] : x [ 0 ][ 1 ]) + 8 +
480 ( HEIGHT ? fm.bytesWidth ( df.format ( data.Item [ j ] .h ) .getBytes () , 0 , df.format ( data.Item [ j ] .h ) .getBytes () .length ) + 8 : 0 ) +
481 ( TEMPER ? fm.bytesWidth ( df.format ( data.Item [ j ] .t ) .getBytes () , 0 , df.format ( data.Item [ j ] .t ) .getBytes () .length ) + 8 : 0 ) ,
482 ( pp [ 2 ] ? y [ 2 ][ 1 ] : pp [ 1 ] ? y [ 1 ][ 1 ] : y [ 0 ][ 1 ]) + fm.getHeight () / 4
483 ) ;
484 }
485 }
486 }
487 return ( true ) ;
488 }
489
490 /**
491 * 功能:画V-3Theta的背景图
492 * 参数:
493 * g - 图形设备
494 * left,top - 背景图的左上角位置
495 * data - 站点的V-3Theta数据
496 * 返回值:
497 * 无
498 */
499 public void background ( Graphics2D g,int left,int top, V3ThetaData data ) {
500 DecimalFormat df = new DecimalFormat ( "0.#" ) ;
501 DecimalFormat dfInt = new DecimalFormat ( "0" ) ;
502 DecimalFormat dfDate = new DecimalFormat ( "00" ) ;
503
504 int x, y;
505 double maxLayerValue = 1032 ;
506 double minLayerValue = 100 ;
507 Rectangle r = new Rectangle ( 0 , 0 , 1024 , 768 ) ;
508 r = g.getClipBounds ( r ) ;
509 g.setColor ( Color.white ) ;
510 g.fillRect ( 0 , 0 , 1024 , 768 ) ;
511 g.setColor ( Color.black ) ;
512 Font fnt = new Font ( "Times New Roman" , Font.BOLD, 11 ) ;
513 g.setFont ( fnt ) ;
514 FontMetrics fm = g.getFontMetrics () ;
515 double qMin = minTheta ( data ) ;
516 double qMax = maxTheta ( data ) ;
517 double LayerIndex [] = { 100 , 150 , 200 , 250 , 300 , 400 , 500 , 600 , 700 , 850 , 925 , 1000 } ;
518 Vector layers = new Vector () ;
519 for ( int i= 0 ;i< 12 ;i++ ) {
520 layers.add ( df.format ( LayerIndex [ i ])) ;
521 }
522
523 //数据包含的层次 - 纵坐标
524 int x0, y0;
525 for ( int i= 0 ;i<data.Layers;i++ )
526 if ( SPECIAL || //显示所有层次
527 layers.indexOf ( df.format ( data.Item [ i ] .layer )) != - 1 ) { //是固定层次
528 x0 = left;
529 if ( LOGP ) {
530 y0 = Integer.parseInt ( dfInt.format ( top + ( Math.log ( data.Item [ i ] .layer ) -Math.log ( minLayerValue )) / ( Math.log ( maxLayerValue ) -Math.log ( minLayerValue )) *yZoom )) ;
531 }
532 else {
533 y0 = Integer.parseInt ( dfInt.format ( top + ( data.Item [ i ] .layer-minLayerValue ) / ( maxLayerValue-minLayerValue ) *yZoom )) ;
534 }
535 // if( i != 11 )
536 g.setColor ( Color.black ) ;
537 g.drawString (
538 df.format ( data.Item [ i ] .layer ) ,
539 x0 - fm.bytesWidth ( df.format ( data.Item [ i ] .layer ) .getBytes () , 0 , df.format ( data.Item [ i ] .layer ) .getBytes () .length ) - 8 ,
540 y0 + fm.getHeight () / 2 ) ;
541 x = Integer.parseInt ( dfInt.format ( left + ( qMax - qMin ) * xZoom )) ;
542 if ( LOGP ) {
543 y = Integer.parseInt ( dfInt.format ( top + ( Math.log ( data.Item [ i ] .layer ) -Math.log ( minLayerValue )) / ( Math.log ( maxLayerValue ) -Math.log ( minLayerValue )) *yZoom )) ;
544 }
545 else {
546 y = Integer.parseInt ( dfInt.format ( top + ( data.Item [ i ] .layer-minLayerValue ) / ( maxLayerValue-minLayerValue ) *yZoom )) ;
547 }
548 if ( BACKGROUND ) { //纵坐标网格线
549 g.setColor ( Color.gray ) ;
550 g.drawLine ( x0, y0, x, y ) ;
551 }
552 }
553
554 //3Theta值 - 横坐标
555 for ( int i= ( new Double ( qMin )) .intValue () ;i<= ( new Double ( qMax )) .intValue () ;i=i+ 10 ) {
556 x0 = Integer.parseInt ( dfInt.format ( left + ( i - qMin ) * xZoom )) ;
557 if ( LOGP ) {
558 y0 = Integer.parseInt ( dfInt.format ( top + ( Math.log ( minLayerValue ) -Math.log ( minLayerValue )) / ( Math.log ( maxLayerValue ) -Math.log ( minLayerValue )) *yZoom )) ;
559 }
560 else {
561 y0 = Integer.parseInt ( dfInt.format ( top + ( minLayerValue-minLayerValue ) / ( maxLayerValue-minLayerValue ) *yZoom )) ;
562 }
563 x = Integer.parseInt ( dfInt.format ( left + ( i - qMin ) * xZoom )) ;
564 if ( LOGP ) {
565 y = Integer.parseInt ( dfInt.format ( top + ( Math.log ( maxLayerValue ) -Math.log ( minLayerValue )) / ( Math.log ( maxLayerValue ) -Math.log ( minLayerValue )) *yZoom )) ;
566 }
567 else {
568 y = top + yZoom;
569 }
570 if ( BACKGROUND ) { //横坐标网格线
571 g.setColor ( Color.gray ) ;
572 g.drawLine ( x0, y0, x, y ) ;
573 }
574 g.setColor ( Color.black ) ;
575 g.drawString ( df.format ( i ) , x-fm.bytesWidth ( df.format ( i ) .getBytes () , 0 , df.format ( i ) .getBytes () .length ) / 2 ,y+ 5 +fm.getHeight ()) ;
576 }
577
578 //横坐标轴
579 x = Integer.parseInt ( dfInt.format ( left + ( qMax + 5 - qMin ) * xZoom )) ;
580 y = Integer.parseInt ( dfInt.format ( top + yZoom )) ;
581 g.setColor ( Color.black ) ;
582 g.drawLine ( left, top+yZoom, x, y ) ;
583 g.drawLine ( x,y,x- 8 ,y+ 4 ) ; //坐标轴箭头
584 g.drawLine ( x,y,x- 8 ,y- 4 ) ;
585 g.setColor ( Color.black ) ;
586 g.drawString ( "T" , x+ 12 -fm.bytesWidth ( "T" .getBytes () , 0 , "T" .getBytes () .length ) / 2 , y- 6 +fm.getHeight () / 2 ) ;
587 g.drawString ( "(K)" ,x+ 12 -fm.bytesWidth ( "(K)" .getBytes () , 0 , "(K)" .getBytes () .length ) / 2 , y+ 6 +fm.getHeight () / 2 ) ;
588 x = Integer.parseInt ( dfInt.format ( left + ( qMax + 5 - qMin ) * xZoom / 2 )) ;
589 y = Integer.parseInt ( dfInt.format ( top + yZoom + 32 )) ;
590 String sTitle = "V-3Theta" + ( Enabled ? " " +dfDate.format ( Year ) + "-" +dfDate.format ( Month ) + "-" +dfDate.format ( Day ) + " " +dfDate.format ( Hour ) + ( Validate== 0 ? "h " : "+" +Validate+ "h " ) + "(" +data.Station+ ")" : "" ) ;
591 g.drawString ( sTitle, x - fm.bytesWidth ( sTitle.getBytes () , 0 , sTitle.getBytes () .length ) / 2 , y+fm.getHeight () / 2 ) ;
592
593 //纵坐标轴
594 x = left;
595 y = Integer.parseInt ( dfInt.format ( top - 5 * 3.5 )) ;
596 g.setColor ( Color.black ) ;
597 g.drawLine ( left, Integer.parseInt ( dfInt.format ( top+yZoom )) , left, Integer.parseInt ( dfInt.format ( top - 5 * 3.5 ))) ;
598 g.drawLine ( x, y, x+ 4 , y+ 8 ) ; //坐标轴箭头
599 g.drawLine ( x, y, x- 4 , y+ 8 ) ;
600 g.setColor ( Color.black ) ;
601 if ( LOGP ) {
602 g.drawString ( "ln(P)" , x+ 6 , y-fm.getHeight () / 2 ) ;
603 }
604 else {
605 g.drawString ( "P" , x+ 6 , y-fm.getHeight () / 2 ) ;
606 }
607 g.drawString ( "hPa" , x-fm.bytesWidth ( "hPa" .getBytes () , 0 , "hpa" .getBytes () .length ) - 6 , y-fm.getHeight () / 2 ) ;
608
609 //数据线图例
610 g.setColor ( Color.green ) ;
611 sTitle = "Theta" ;
612 x = left;
613 y = Integer.parseInt ( dfInt.format ( top + ( 1 * 100 - 50 ) * 3 / 10 )) ;
614 g.drawString ( sTitle, x+ 35 , y+fm.getHeight () / 4 ) ;
615 g.drawLine ( x+ 10 , y, x+ 30 , y ) ;
616
617 g.setColor ( Color.red ) ;
618 sTitle = "Theta se" ;
619 x = left;
620 y = Integer.parseInt ( dfInt.format ( top + ( 2 * 100 - 50 ) * 3 / 10 )) ;
621 g.drawString ( sTitle, x+ 35 , y+fm.getHeight () / 4 ) ;
622 g.drawLine ( x+ 10 , y, x+ 30 , y ) ;
623
624 g.setColor ( Color.blue ) ;
625 sTitle = "Theta *" ;
626 x = left;
627 y = Integer.parseInt ( dfInt.format ( top + ( 3 * 100 - 50 ) * 3 / 10 )) ;
628 g.drawString ( sTitle, x+ 35 , y+fm.getHeight () / 4 ) ;
629 g.drawLine ( x+ 10 , y, x+ 30 , y ) ;
630 }
631 }
V3ThetaData.java
01 /*******************************************************************************
02 * *
03 * 处理溃变理论(成气院欧阳首承教授创立)的V-3θ单层资料 *
04 * *
05 * PACKAGE: cma.cuit.blowup *
06 * FILENAME: V3ThetaData.java *
07 * LANGUAGE: Java2 v1.4 *
08 * ORIGINAL: c++ *
09 * DESCRIPTION: *
10 * CREATE: 2006-10-02 01:41:18 *
11 * UPDATE: 2006-10-02 *
12 * AUTHOR: 刘泽军 (BJ0773@gmail.com) *
13 * *
14 *******************************************************************************/
15
16 package cma.cuit.blowup;
17
18 public class V3ThetaData { //计算成都气象学院欧阳首承教授的V-3θ图单层的3个θ值
19
20 public static int LAYERS = 72 ; //最多层次数(根据探空资料的TlogP数据,最多有39层)
21 public String Station; //区站号
22 public double Longitude, Latitude, Altitude; //经度 纬度 拔海高度
23 public int Layers = 12 ; //实际层次数,一般为 100 150 200 250 300 400 500 600 700 850 925 1000 标准层
24 public V3ThetaLayer Item [] = { //72层
25 null, null, null, null, null, null, null, null, null, null,
26 null, null, null, null, null, null, null, null, null, null,
27 null, null, null, null, null, null, null, null, null, null,
28 null, null, null, null, null, null, null, null, null, null,
29 null, null, null, null, null, null, null, null, null, null,
30 null, null, null, null, null, null, null, null, null, null,
31 null, null, null, null, null, null, null, null, null, null,
32 null, null } ;
33
34 public V3ThetaData () {
35 for ( int i= 0 ;i<LAYERS;i++ ) {
36 Item [ i ] = new V3ThetaLayer () ;
37 }
38 }
39
40 public String toString () {
41 return ( "" ) ;
42 }
43 }
V3ThetaLayer.java
01 /*******************************************************************************
02 * *
03 * 处理溃变理论(成气院欧阳首承教授创立)的V-3θ单层资料 *
04 * *
05 * PACKAGE: cma.cuit.blowup *
06 * FILENAME: V3ThetaLayer.java *
07 * LANGUAGE: Java2 v1.4 *
08 * ORIGINAL: c++ *
09 * DESCRIPTION: *
10 * CREATE: 2006-10-02 01:36:20 *
11 * UPDATE: 2006-10-02 *
12 * AUTHOR: 刘泽军 (BJ0773@gmail.com) *
13 * *
14 *******************************************************************************/
15
16 package cma.cuit.blowup;
17
18 import java.lang.*;
19 import java.text.DecimalFormat;
20
21 import cma.common.atmos.*;
22 import cma.micaps.diamond.datatype.*;
23 import cma.micaps.diamond.*;
24
25 public class V3ThetaLayer { //计算成都气象学院欧阳首承教授的V-3θ图单层的3个θ值
26 public double value = Formula.VALUE; //缺省值
27 public int layer; //=p
28 public double h,t,t_td; //高度 温度 温度露点差
29 public int dd, ff;
30 public double q1,q2,q3; //位温θ(Theta),假相当位温θse(Thetase),θ*(Theta*)
31 public V3ThetaLayer ( int _layer,double _h,double _t,double _t_td,int _dd,int _ff ) {
32 reset ( _layer, _h, _t, _t_td, _dd, _ff ) ;
33 proc () ;
34 }
35 public void reset ( int _layer,double _h,double _t,double _t_td,int _dd,int _ff ) {
36 DecimalFormat df = new DecimalFormat ( "0" ) ;
37 layer = _layer;
38 h = DiamondGG.VALUE == _h ? value : _h;
39 t = DiamondGG.VALUE == _t ? value : _t;
40 t_td = DiamondGG.VALUE == _t_td ? value : _t_td;
41 dd = DiamondGG.VALUE == _dd ? Integer.parseInt ( df.format ( value )) : _dd;
42 ff = DiamondGG.VALUE == _ff ? Integer.parseInt ( df.format ( value )) : _ff;
43 q1 = value;
44 q2 = value;
45 q3 = value;
46 }
47 public V3ThetaLayer () {
48 reset ( 0 , value, value, value, ( new Double ( value )) .intValue () , ( new Double ( value )) .intValue ()) ;
49 }
50 void proc () {
51 double p = layer, td = value;
52 if ( t == value || t_td == value ) td = value;
53 else td = t - t_td;
54 q1 = Formula.blowupTheta1 ( p,t,td ) ;
55 q2 = Formula.blowupTheta2 ( p,t,td ) ;
56 q3 = Formula.blowupTheta3 ( p,t,td ) ;
57 }
58 }