天体の基本理解に、「Illustratorで星図と星座を描くスクリプト」
を参考に Java で静的な星図を描画してみる。作者が事前に加工したタブセパレーション星データ
constellation_boundary.txt
constellation_line.txt
star_map.txt
をそのまま利用。
java なので さらにアンドロイドアプリへ発展。...▼

constellation_boundary.txt
constellation_line.txt
star_map.txt
をそのまま利用。



java なので さらにアンドロイドアプリへ発展。...▼
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/
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/