« アプリに git リビジョン情報を埋め込む | メイン | java の interface(CallBack)備忘録 »

java

星図描画

 天体の基本理解に、こちら「Illustratorで星図と星座を描くスクリプト」 を参考に Java で静的な星図を描画してみる。作者が事前に加工したタブセパレーション星データ
constellation_boundary.txt
constellation_line.txt
star_map.txt
をそのまま利用。
メルカトル図法
正距方位図法 北天
正距方位図法 南天
java なので さらにアンドロイドアプリへ発展。...▼
Star.java (星データの読み込み + 星クラス)
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.List;
import java.awt.Point;

/*
* 星データ構造体
*/
class Star{
	int    hip;   //ヒッパルコスHIP番号:1~120416
	int    vmag;  //視等級   索引0~8に収める変換 冗長ロジック
	Point  prd;   //100倍 赤経right ascension,赤緯declination
	String proper;//星名

	int getRas(){ return prd.x; };//赤経
	int getDsc(){ return prd.y; };//赤緯

	/**
	* コンストラクタ
	* @param s 初期化配列
	* s[0] ヒッパルコスHIP番号:1~120416
	* s[1] 赤経
	* s[2] 赤緯
	* s[3] 視等級
	*       0.5未満 明るい
	*       0.5~1未満
	*       1~1.5未満
	*       1.5~2未満
	*       2
	*       3
	*       4
	*       5
	*       6 暗い
	* s[4] 星名
	*/
	Star( String[] s )throws java.lang.NumberFormatException{
		hip = Tool.IntegerParseInt( s[0] );
		prd = new Point( Tool.FloatParseFloatx100( s[1] ), Tool.FloatParseFloatx100( s[2] ) );
		vmag= Tool.FloatParseFloatx100( s[3] );
		proper= ( 4 < s.length ) ?  s[4] : null;
		if( 0 < Tool.listMessages.size() ){
			String str= Tool.listMessages.toString();
			Tool.listMessages.clear();
			throw new java.lang.NumberFormatException( str );
		}
	}

	/**
	* @param s 入力ファイル名
	* @return List<星>
	*/
	static List<Star> load( String s ){
		List<Star> list= new java.util.ArrayList<Star>();
		int i= 0;
		BufferedReader reader= null;
		try{
			reader= new BufferedReader( new InputStreamReader(
			new FileInputStream( s ), Charset.forName( "UTF-8" ) ) );//Shift-JIS

			String line;
			while( ( null != ( line = reader.readLine() ) ) ){
			//	System.out.print( String.format( "[%s]", line ) );
				try{
					list.add( new Star( line.split( "\t" ) ) );
				}catch( Exception e ){
					System.err.println( String.format( "Skip:%d %s", i, e.getMessage() ) );
				}
				i++;
			}
		}catch( IOException e ){
			e.printStackTrace();
		}finally{
			try{
				if( reader != null )reader.close();
			}catch( IOException e ){
				System.err.println( e );
			}
		}
		System.out.println( String.format( "ok:%5d / in:%5d [%s]", list.size(), i, s ) );
		return list;
	}

	public static List<Star> loadFile(){
		return load( "star_map.txt" );
	}

	public static void main( String[] args ){
		List<Star> list= loadFile();
	}
}
ConstellationLine (ラインデータの読み込み + ラインクラス)
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.List;
import java.awt.Point;

/*
* 星座線 境界線 データ構造体
*/
class ConstellationLine{
	int id;  //星座ID
	Point ps;//100倍 赤経right ascension,赤緯declination 開点
	Point pe;//100倍 赤経right ascension,赤緯declination 終点

	int getRasStart(){ return ps.x; };
	int getDscStart(){ return ps.y; };
	int getRasEnd(){ return pe.x; };
	int getDscEnd(){ return pe.y; };

	/**
	* コンストラクタ
	* @param s 初期化配列
	* s[0] 星座ID
	* s[1] 赤経始点
	* s[2] 赤緯始点
	* s[3] 赤経終点
	* s[4] 赤緯終点
	*/
	ConstellationLine( String[] s )throws java.lang.NumberFormatException{
		id = Tool.IntegerParseInt( s[0] );
		ps = new Point( Tool.FloatParseFloatx100( s[1] ), Tool.FloatParseFloatx100( s[2] ) );
		pe = new Point( Tool.FloatParseFloatx100( s[3] ), Tool.FloatParseFloatx100( s[4] ) );
		if( 0 < Tool.listMessages.size() ){
			String str= Tool.listMessages.toString();
			Tool.listMessages.clear();
			throw new java.lang.NumberFormatException( str );
		}
	}

	/**
	* @param s 入力ファイル名
	* @return List<線分>
	*/
	static List<ConstellationLine> load( String s ){
		List<ConstellationLine> list= new java.util.ArrayList<ConstellationLine>();
		int i= 0;
		BufferedReader reader= null;
		try{
			reader= new BufferedReader( new InputStreamReader(
			new FileInputStream( s ), Charset.forName( "UTF-8" ) ) );//Shift-JIS

			String line;
			while( ( null != ( line = reader.readLine() ) ) ){
				try{
					list.add( new ConstellationLine( line.split( "\t" ) ) );
				}catch( Exception e ){
					System.err.println( String.format( "Skip:%d %s", i, e.getMessage() ) );
				}
				i++;
			}
		}catch( IOException e ){
			e.printStackTrace();
		}finally{
			try{
				if( reader != null )reader.close();
			}catch( IOException e ){
				System.err.println( e );
			}
		}
		System.out.println( String.format( "ok:%5d / in:%5d [%s]", list.size(), i, s ) );
		return list;
	}

	public static List<ConstellationLine> loadFile(){
		return load( "constellation_line.txt" );
	}

	public static List<ConstellationLine> loadFileBoundary(){
		return load( "constellation_boundary.txt" );
	}

	public static void main( String[] args ){
		List<ConstellationLine> list= loadFile();
		List<ConstellationLine> list2= loadFileBoundary();
	}
}
Tool.java (読込データパース共通メソッド)
import java.util.List;

class Tool{
	static java.util.List<String> listMessages= new java.util.ArrayList<String>();
	static int IntegerParseInt( String s ){
		int i= 0;
		try{
			i= Integer.parseInt( s );
		}catch( java.lang.NumberFormatException e ){
			listMessages.add( e.getMessage() );
		}
		return i;
	}

	static int FloatParseFloatx100( String s ){
		int i= 0;
		try{
			i= (int)( 100F * Float.parseFloat( s ) );
		}catch( java.lang.NumberFormatException e ){
			listMessages.add( e.getMessage() );
		}
		return i;
	}
}
Astro.java (描画フレーム)
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

class Astro extends Frame{
	static enum TYPE{
		Mercator //全天(赤緯-60~60の範囲のみ)メルカトル図法
	,   North    //北天 正距方位図法
	,   South    //南天 正距方位図法
	};
	static double DEG2RAD = Math.PI / 180;//度数からラジアンに変換
	static int ZOOM = 4;//座標倍率
	static List<Star> listStar;//星データ
	static List<ConstellationLine> listConstellationLine;//星座線
	static List<ConstellationLine> listConstellationBoundary;//星座境界線

	MyCanvas canvas;//複数ウィンドウ可能
	TYPE type= TYPE.Mercator;

	public static void main( String[] args ){
		listStar= Star.loadFile();
		listConstellationLine= ConstellationLine.loadFile();
		listConstellationBoundary= ConstellationLine.loadFileBoundary();
		new Astro();
	}

	/** constructor */
	Astro(){
		addWindowListener( new java.awt.event.WindowAdapter(){
			public void windowClosing( java.awt.event.WindowEvent e ){
				System.exit( 0 );
			}
		} );

		Button button1= new Button( "Mercator" );
		button1.addActionListener( new ActionListener(){
			@Override
			public void actionPerformed( ActionEvent e ){
				type= TYPE.Mercator;
				canvas.repaint();
			}
		});
		Button button2= new Button( "North" );
		button2.addActionListener( new ActionListener(){
			@Override
			public void actionPerformed( ActionEvent e ){
				type= TYPE.North;
				canvas.repaint();
			}
		});
		Button button3= new Button( "South" );
		button3.addActionListener( new ActionListener(){
			@Override
			public void actionPerformed( ActionEvent e ){
				type= TYPE.South;
				canvas.repaint();
			}
		});

		Panel panel= new Panel();
		panel.setBackground( Color.BLACK );
		panel.setLayout( new FlowLayout( FlowLayout.CENTER ) );
		panel.add( button1 );
		panel.add( button2 );
		panel.add( button3 );
		add( "South", panel );

		canvas = new MyCanvas();
		add( "Center", canvas );
		setVisible( true );
		pack();
	}

	class MyCanvas extends Canvas{
		public MyCanvas(){
			setBackground( Color.BLACK );
			setPreferredSize( new Dimension( 800, 600 ) );
		}

		public void paint( Graphics graphics ){
			super.paint( graphics );

			Dimension dim= getSize();
			graphics.setColor( Color.GRAY.darker() );
			System.out.println( String.format( "%dx%d", dim.width, dim.height ) );

			switch( type ){
			case Mercator:
				graphics.translate( dim.width/2+(ZOOM*180), dim.height/2 );
				drawGrid( graphics );
				drawStar( graphics, type );//星 描画
				drawConstellation( graphics, type, listConstellationLine, Color.YELLOW.darker().darker().darker() );
				drawConstellation( graphics, type, listConstellationBoundary, Color.BLUE.darker() );
				break;
			case North:
			case South:
				graphics.translate( dim.width/2, dim.height/2 );
				drawGrid2( graphics );
				drawStar( graphics, type );//星 描画
				drawConstellation( graphics, type, listConstellationLine, Color.YELLOW.darker().darker().darker() );
				drawConstellation( graphics, type, listConstellationBoundary, Color.BLUE.darker() );
				break;
			}
		}
	}

	/**
	* メルカトル図法用グリッド線
	* @param graphics
	*/
	static void drawGrid( Graphics graphics ){
		graphics.setColor( Color.RED.darker().darker() /*.darker()*/ );

		//横ライン 赤緯(北天60°~南天-60°)
		for( int y= 60; y >= -60; y-=10 ){
			graphics.drawLine( -360 * ZOOM, -y * ZOOM, 0, -y * ZOOM );
			graphics.drawString( String.format( "%4d", y ), 0, -y * ZOOM );//scale
		}

		//縦ライン 赤経(左端-360°~0°右端)
		for( int x= -360; x <= 0; x+=10 ){
			graphics.drawLine( x * ZOOM, -60 * ZOOM, x * ZOOM, 60 * ZOOM );
		}
	}

	/**
	* 正距方位図法(円形)グリッド線 北天/南天 共通
	* @param graphics
	*/
	static void drawGrid2( Graphics graphics ){
		graphics.setColor( Color.RED.darker().darker() /*.darker()*/ );

		//同心円 中央ゼロ座標 赤緯(±90°~0°)
		for( int r= 0; r <= 90; r+=10 ){
			drawCircle( graphics, 0, 0, r * ZOOM );
		}

		//放射状ライン 赤経(0° ~ 360°)
		int r= 90 * ZOOM;//最大半径←90度
		for( int k= 0; k <= 360; k+= 10 ){//10°毎
			int x= (int)( Math.cos( k * DEG2RAD ) * r );
			int y= (int)( Math.sin( k * DEG2RAD ) * r );
			graphics.drawLine( 0, 0, x, y );
		}
	}

	static void drawCircle( Graphics graphics, int x, int y, int r ){
		int top = y - r;
		int left = x - r;
		int width = r * 2;
		int height = r * 2;
		graphics.drawArc( top, left, width, height, 0, 360 );
	}

	/**
	* (赤経,赤緯) → 正距方位図法(x,y)座標変換
	* @param iRas 赤経x100
	* @param iDsc 赤緯x100
	* @return (x,y)座標
	*/
	static int calcNorthX( int iRas, int iDsc ){//(円形)北天 正距方位図法 ↓0h ←6h
		return (int)( Math.cos( ( iRas-27000 )/100 * DEG2RAD ) * ( 9000-iDsc )/100 );
	}
	static int calcNorthY( int iRas, int iDsc ){//             * 中心からの距離
		return (int)( Math.sin( ( iRas-27000 )/100 * DEG2RAD ) * ( 9000-iDsc )/100 );
	}
	static int calcSouthX( int iRas, int iDsc ){//(円形)南天 正距方位図法 ↑0h ←6h
		return (int)( Math.cos( ( iRas-9000 )/100 * DEG2RAD ) * (-9000-iDsc )/100);
	}
	static int calcSouthY( int iRas, int iDsc ){
		return (int)(-Math.sin( ( iRas-9000 )/100 * DEG2RAD ) * (-9000-iDsc )/100);
	}

	/**
	* 星座線 境界線
	* @param graphics
	* @param list Data List
	* @param c    Line Color
	*/
	static void drawConstellation( Graphics graphics, TYPE type, List<ConstellationLine> list, Color c ){
		graphics.setColor( c );
		int xS= 0;
		int yS= 0;
		int xE= 0;
		int yE= 0;
		int tp= 0;
		for( ConstellationLine line: list ){
			xS= line.getRasStart();
			yS= line.getDscStart();
			xE= line.getRasEnd();
			yE= line.getDscEnd();
			switch( type ){
			case Mercator:
				if( -6000 > yS || yS > 6000 || -6000 > yE || yE > 6000 ) continue;//緯度±60°超スキップ
				if( 27000 < ( (xS < xE) ? xE - xS : xS - xE ) ) continue;//270°超7個スキップ
				xS= -xS * ZOOM / 100;
				yS= -yS * ZOOM / 100;
				xE= -xE * ZOOM / 100;
				yE= -yE * ZOOM / 100;
				break;
			case North://(円形)北天 正距方位図法 ↓0h ←6h
				if( 0 > line.getDscStart() ) continue;
				tp= calcNorthX( xS, yS ) * ZOOM;
				yS= calcNorthY( xS, yS ) * ZOOM;
				xS= tp;
				tp= calcNorthX( xE, yE ) * ZOOM;
				yE= calcNorthY( xE, yE ) * ZOOM;
				xE= tp;
				break;
			case South://(円形)南天 正距方位図法 ↑0h ←6h
				if( 0 < line.getDscStart() ) continue;
				tp= calcSouthX( xS, yS ) * ZOOM;
				yS= calcSouthY( xS, yS ) * ZOOM;
				xS= tp;
				tp= calcSouthX( xE, yE ) * ZOOM;
				yE= calcSouthY( xE, yE ) * ZOOM;
				xE= tp;
				break;
			}
			graphics.drawLine( xS, yS, xE, yE );
		}
	}

	/**
	* 星 描画
	* @param graphics
	*/
	static void drawStar( Graphics graphics, TYPE type ){
		graphics.setColor( Color.WHITE );
		int v;
		for( Star s: listStar ){
			int x= 0;
			int y= 0;
			switch( type ){
			case Mercator://全天(赤緯-60~60の範囲のみ)メルカトル図法
				x= -s.getRas() * ZOOM / 100;//赤経★逆さま?
				y= -s.getDsc() * ZOOM / 100;//赤緯逆さま?
				break;
			case North://(円形)北天 正距方位図法 ↓0h ←6h
				if( 0 > s.getDsc() ) continue;
				x= calcNorthX( s.getRas(), s.getDsc() ) * ZOOM;
				y= calcNorthY( s.getRas(), s.getDsc() ) * ZOOM;
				break;
			case South://(円形)南天 正距方位図法 ↑0h ←6h
				if( 0 < s.getDsc() ) continue;
				x= calcSouthX( s.getRas(), s.getDsc() ) * ZOOM;
				y= calcSouthY( s.getRas(), s.getDsc() ) * ZOOM;
				break;
			}

			if( null != s.proper ){//星名 TODO:座標補正
				graphics.setColor( Color.BLUE.darker() );
				graphics.drawString( s.proper, x, y );
			}

			if( 300 < s.vmag ){//暗い
				v= 1;
			//	graphics.setColor( Color.WHITE.darker().darker() ); //Color.GRAY.brighter() );
				graphics.setColor( Color.GRAY );
			}else if( 200 < s.vmag ){
				v= 1;
				graphics.setColor( Color.WHITE );
			}else if( 40 < s.vmag ){
				v= 3;
				graphics.setColor( Color.WHITE );
			}else{
				v= 5;
				graphics.setColor( Color.WHITE );
			}//明るい

			graphics.fillOval( x, y, v, v );
		}
	}
}
javac -encoding UTF8 Star.java
前出のデータとコンパイル済のファイルを同じディレクトリに配置して
java Star
で起動。

●参考情報
阿南市科学センターで公開される星座早見PDF
http://ananscience.jp/science/tenmonkan/gallery.htm
http://ananscience.jp/science/tenmonkan/hayamiban/ASC_hayami_201803ver.pdf

星空PDF生成
https://hoshifuru.jp/pdf/

トラックバック

このエントリーのトラックバックURL:
https://www.remix.asia/cgi/mt/mt-tb.cgi/7747

コメントを投稿

(いままで、ここでコメントしたことがないときは、コメントを表示する前にこのブログのオーナーの承認が必要になることがあります。承認されるまではコメントは表示されません。そのときはしばらく待ってください。)