Old Project, New Version

I’ve been fooling with this thing for ages and finally have a version that agrees with what I initially had in mind. That doesnt mean its great, just that it works ok. First thing you need to do is get a worldmap, any will do and it can even be a map of another planet if you want. Put this jpg, or png into a data folder within the sketch folder. The sketch folder is named anything you want.

What this program does is demonstrate how the observation of 3 stars from some arbitrary location can represent a position fix. These 3 observations are represented as circles, defined by altitude, declination, GHA. GHA and declination are dependant on which star is observed and altitude varies according to location And time.

When the program starts, a java swing gui thing starts and the processing draw window starts. Use the mouse to move the swing gui off to the side. The gui has 3 sliders, the top is GHA, the second is altitude, and the third is declination. There is a on/off box for globe and one for map. Click either to see how these circles are represented. Adjust the sliders and then click Star1. This circle is drawn as red. Star2 is white, and Star3 is blue.

You can play with the sliders to make a hypothetical set of observations or you can use an ephemeris program to get GHA,dec,alt for specific stars and locations. Keep in mind this is a concept demo and not accurate enough for actual naviigation.


//package processing.test.fixdemo;

import processing.core.*; 
import processing.data.*; 
import processing.event.*; 
import processing.opengl.*; 

import java.awt.*; 
import javax.swing.*; 
import java.awt.event.*; 
import javax.swing.event.*; 

import java.util.HashMap; 
import java.util.ArrayList; 
import java.io.File; 
import java.io.BufferedReader; 
import java.io.PrintWriter; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.io.IOException; 

///public class FixDemo extends PApplet {

/*
 The sphere is drawn using the example from processing/example/texturesphere
 The green circle is drawn based on code from Andres Ruiz
 I dont claim anything other than I fused it all together
*/







MyPanel controlPanel;
PImage texmap;
PGraphics texture;
boolean Star1;
boolean Star2;
boolean Star3;
boolean Globe;
boolean Map;
float[] x = new float[3600];
float[] y = new float[3600];
int w;
// double GHA = Math.toRadians(162.3366651);
double GHA = Math.toRadians(214.21545f);
double GHA1 = Math.toRadians(214.21545f);
double GHA2 = Math.toRadians(279.2346f);
double GHA3 = Math.toRadians(165.932325f);
// double dec = Math.toRadians(42); 
double dec = Math.toRadians(19.335f);
double dec1 = Math.toRadians(19.335f);
double dec2 = Math.toRadians(38.758f);
double dec3 = Math.toRadians(61.909f);
 //    double dec = mouseY;
double Be = Math.toRadians(35.4000f);
double Le = Math.toRadians(26.452837f); 
double[]vv = new double[3]; //, vy[3], vyz[3];
double[]vy = new double[3];
double[]vyz = new double[3];
double[]wpt = new double[3600];
float[]WPT = new float[3600];
double[][]My = new double[3][3];
double[][]Mz = new double[3][3]; 
int sDetail = 65;  // Sphere detail setting
float rotationX = 0;
float rotationY = 0;
float velocityX = 0;
float velocityY = 0;
//float globeRadius = 600;
float pushBack = -100; 
float globeRadius = 700;
float[] cx, cz, sphereX, sphereY, sphereZ;
float sinLUT[];
float cosLUT[];
float SINCOS_PRECISION = 0.5f;
int SINCOS_LENGTH = PApplet.parseInt(360.0f / SINCOS_PRECISION);
int [] colors = new int[7];
float alt = radians(55.53f);
float alt1 = radians(55.53f);
float alt2 = radians(64.648f);
float alt3 = radians(38.18f);
//Controls controls;
boolean released = false;
Controls controls;
HorizontalControl controlX;
int showControls;
boolean draggingZoomSlider = false;
float zoom = -90.0f;
float tzoom = -100.0f;

public void setup() {
// size(400, 300, P3D);
 // size(600, 500, P3D);
      size(1275, 750, P3D);  
  
  noFill();   
    // texmap = loadImage("world32k.jpg"); 
       //   texmap = loadImage("starmap_4k2B2b.jpg"); 
  texmap = loadImage("earthmap1.jpg"); 
//  texmap = loadImage("alphatest.png");
  texture = createGraphics(texmap.width, texmap.height);
  controls = new Controls();
  controlX = new HorizontalControl();
  showControls = 1;    
  initStars();
  getPoints();
  initializeSphere(sDetail);
//  Map = false;  
  JFrame frame =new JFrame("Controls");
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  controlPanel = new MyPanel();
  controlPanel.setOpaque(true); //content panes must be opaque
  frame.setContentPane(controlPanel);
  //Display the window.
  frame.pack();
  frame.setVisible(true);  
  
}

int timer=0;

public void draw() {    
  background(0); 
  x = new float[3600];
  y = new float[3600];  
// renderGlobe(); 
      if (mousePressed) {
     if( (showControls == 1) && (controls.isZoomSliderEvent(mouseX, mouseY)) || ( showControls == 1 && controlX.isZoomSliderEvent(mouseX,mouseY))) {
          draggingZoomSlider = true;       
          zoom = controls.getZoomValue(mouseY);
          tzoom = controlX.getZoomValue(mouseX,mouseY);      
        }       
           // MousePress - Rotation Adjustment
      else if (!draggingZoomSlider) {
        if (released == true){
         velocityX += (mouseY-pmouseY) * 0.01f;
         velocityY -= (mouseX-pmouseX) * 0.01f;
        // draggingZoomSlider = false;
         }
       
     } 
 
      }  
      
      
  if (timer !=0)
  {
     if (millis()>=timer)
        if (Star1 == true) {
              GHA1 = GHA;
              alt1 = alt;
              dec1 = dec;
              initStars();
              initializeSphere(sDetail);
              getPoints();
              Star1 = false;
        }  
     
        if (Star2 == true) {
              GHA2 = GHA;
              alt2 = alt;
              dec2 = dec;
              initStars();
              initializeSphere(sDetail);
              getPoints();
              Star2 = false;
        } 
     
         if (Star3 == true) {
              GHA3 = GHA;
              alt3 = alt;
              dec3 = dec;
              initStars();
              initializeSphere(sDetail);
              getPoints();
              Star3 = false;
       } 
     }
     

     
     if (Globe == true) {
         renderGlobe();  
     }else if(Globe == false && Map == true) {
         image(texture,0,0,width,height);   
     }  
 }
 
 





//Funcition to convert double[] to float[]
public float[] toFloatArray(double[] arr) {
  if (arr == null) return null;
  int n = arr.length;
  float[] ret = new float[n];
  for (int i = 0; i < n; i++) {
  ret[i] = (float)arr[i];
  }
  return ret;
}
// end of function to convert double[] to float[]

 public double[] VectorSpherical2Cartesian(double B, double L){
  
   double v[] = new double[3];
   v[0] = Math.cos(B) * Math.cos(L);
   v[1] = Math.cos(B) * Math.sin(L);
   v[2] = Math.sin(B);

   return(v);
   
 }

public double C2ELat( double x, double y, double z )
{
  double[]res = new double[3];
  res[0] = Math.sqrt( x*x+y*y+z*z);  //R
//*B = ASIN(z/(*R));
  res[1] = Math.atan2( z, Math.sqrt(x*x+y*y) ); //B
  res[2] = Math.atan2( y, x ); //L

  return (res[1]);

}

public double C2ELon( double x, double y, double z )
{
  double[]res = new double[3];
  res[0] = Math.sqrt( x*x+y*y+z*z);  //R
  res[1] = Math.atan2( z, Math.sqrt(x*x+y*y) ); //B
  res[2] = Math.atan2( y, x ); //L

  return (res[2]);

}
 

public double[] E2C( double B, double L, double R )
{
  double[]res = new double[3];
  
  res[0] = R*Math.cos((B))*Math.cos((L));
  res[1] = R*Math.cos((B))*Math.sin((L));
  res[2] = R*Math.sin((B));
 

 
  return(res);
}
 
 public double[][] Rx( double a, double[][] M ){

  M[0][0] = 1.0f;
  M[1][0] = 0.0f;
  M[2][0] = 0.0f;
  M[0][1] = 0.0f;
  M[1][1] = Math.cos(a); //Math.cos(Math.toRadians(a));
  M[2][1] = Math.sin(a); //Math.sin(Math.toRadians(a));
  M[0][2] = 0.0f;
  M[1][2] = -Math.sin(a); //-Math.sin(Math.toRadians(a));
  M[2][2] = Math.cos(a); //Math.cos(Math.toRadians(a));
  
  return(M);
}

public double[][] Ry( double a, double[][] M ){

  M[0][0] = Math.cos(a);
  M[1][0] = 0.0f;
  M[2][0] = -Math.sin(a);
  M[0][1] = 0.0f;
  M[1][1] = 1.0f;
  M[2][1] = 0.0f;
  M[0][2] = Math.sin(a);
  M[1][2] = 0.0f; 
  M[2][2] = Math.cos(a);
  
  return(M);
}

public double[][] Rz( double a, double[][] M ){

  M[0][0] = Math.cos(a); //Math.cos(a);
  M[1][0] = Math.sin(a);
  M[2][0] = 0.0f;
  M[0][1] = -Math.sin(a);
  M[1][1] = Math.cos(a);
  M[2][1] = 0.0f;
  M[0][2] = 0.0f; 
  M[1][2] = 0.0f; 
  M[2][2] = 1.0f;
  
  return(M);
}
 
public double[] MatrixVecProd( double[][] A, double[] v, double[] res ) {

  int i,j;
  int n = 3;

  for( i=0; i<n; i++ ) {
    res[i] = 0.0f;
    for( j=0; j<n; j++ ) {
    res[i] += A[i][j]*v[j];
   
  }
}

  return (res);
}


public void initStars(){
   
   texture.beginDraw();
   texture.background(texmap);
   texture.endDraw();
}



public void initializeSphere(int res)
{
  sinLUT = new float[SINCOS_LENGTH];
  cosLUT = new float[SINCOS_LENGTH];

  for (int i = 0; i < SINCOS_LENGTH; i++) {
    sinLUT[i] = (float) Math.sin(i * DEG_TO_RAD * SINCOS_PRECISION);
    cosLUT[i] = (float) Math.cos(i * DEG_TO_RAD * SINCOS_PRECISION);
  }

  float delta = (float)SINCOS_LENGTH/res;
  float[] cx = new float[res];
  float[] cz = new float[res];
  
  // Calc unit circle in XZ plane
  for (int i = 0; i < res; i++) {
    cx[i] = -cosLUT[(int) (i*delta) % SINCOS_LENGTH];
    cz[i] = sinLUT[(int) (i*delta) % SINCOS_LENGTH];
  }
  
  // Computing vertexlist vertexlist starts at south pole
  int vertCount = res * (res-1) + 2;
  int currVert = 0;
  
  // Re-init arrays to store vertices
  sphereX = new float[vertCount];
  sphereY = new float[vertCount];
  sphereZ = new float[vertCount];
  float angle_step = (SINCOS_LENGTH*0.5f)/res;
  float angle = angle_step;
  
  // Step along Y axis
  for (int i = 1; i < res; i++) {
    float curradius = sinLUT[(int) angle % SINCOS_LENGTH];
    float currY = -cosLUT[(int) angle % SINCOS_LENGTH];
    for (int j = 0; j < res; j++) {
      sphereX[currVert] = cx[j] * curradius;
      sphereY[currVert] = currY;
      sphereZ[currVert++] = cz[j] * curradius;
    }
    angle += angle_step;
  }
  sDetail = res;
}

// Generic routine to draw textured sphere
public void texturedSphere(float r, PGraphics t) {
  int v1,v11,v2;
  r = (r + 240 ) * 0.33f;
  beginShape(TRIANGLE_STRIP);
  texture(t);
  float iu=(float)(t.width-1)/(sDetail);
  float iv=(float)(t.height-1)/(sDetail);
  float u=0,v=iv;
  for (int i = 0; i < sDetail; i++) {
    vertex(0, -r, 0,u,0);
    vertex(sphereX[i]*r, sphereY[i]*r, sphereZ[i]*r, u, v);
    u+=iu;
  }
  vertex(0, -r, 0,u,0);
  vertex(sphereX[0]*r, sphereY[0]*r, sphereZ[0]*r, u, v);
  endShape();   
  
  // Middle rings
  int voff = 0;
  for(int i = 2; i < sDetail; i++) {
    v1=v11=voff;
    voff += sDetail;
    v2=voff;
    u=0;
    beginShape(TRIANGLE_STRIP);
    texture(t);
    for (int j = 0; j < sDetail; j++) {
      vertex(sphereX[v1]*r, sphereY[v1]*r, sphereZ[v1++]*r, u, v);
      vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2++]*r, u, v+iv);
      u+=iu;
    }
  
    // Close each ring
    v1=v11;
    v2=voff;
    vertex(sphereX[v1]*r, sphereY[v1]*r, sphereZ[v1]*r, u, v);
    vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2]*r, u, v+iv);
    endShape();
    v+=iv;
  }
  u=0;
  
  // Add the northern cap
  beginShape(TRIANGLE_STRIP);
  texture(t);
  for (int i = 0; i < sDetail; i++) {
    v2 = voff + i;
    vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2]*r, u, v);
    vertex(0, r, 0,u,v+iv);    
    u+=iu;
  }
  vertex(sphereX[voff]*r, sphereY[voff]*r, sphereZ[voff]*r, u, v);
  endShape();
  
}


public void renderGlobe() {
 
  pushMatrix();
  translate(width/2, height/2, pushBack);
  pushMatrix();
  noFill();
  stroke(255,200);
  strokeWeight(2);
  smooth();
  popMatrix();
  lights();    
  pushMatrix();
  
  rotateX( radians(-rotationX) );
   
  rotateY( radians( - rotationY) );
  
  fill(200);
  noStroke();
  textureMode(IMAGE);  
  texturedSphere(globeRadius, texture);
  popMatrix();  
  popMatrix();
  rotationX += velocityX;
  rotationY += velocityY;
  velocityX *= 0.95f;
  velocityY *= 0.95f;
     
}
    //**************************************************************
//  Copy-paste from GuiGeene 
//**************************************************************

public class MyPanel extends JPanel {
  private JButton jcomp1;
  private JButton jcomp2;
  private JButton jcomp3;
  private JSlider jcomp4;
  private JSlider jcomp5;
  private JSlider jcomp6;
  private JButton jcomp7;
  private JButton jcomp8;

  public MyPanel() {
    //construct components

    jcomp1 = new JButton ("Star 1");
    jcomp2 = new JButton ("Star 2");
    jcomp3 = new JButton ("Star 3");
    jcomp4 = new JSlider (0, 360);
    jcomp5 = new JSlider (-90, 90);
    jcomp6 = new JSlider (-90, 90);
    jcomp7 = new JButton ("Globe");
    jcomp8 = new JButton ("Map");

    jcomp1.addActionListener(new Button1Click());
        jcomp2.addActionListener(new Button2Click());
                jcomp3.addActionListener(new Button3Click());
    jcomp4.addChangeListener(new HSlider1Change());
    jcomp5.addChangeListener(new HSlider2Change());
    jcomp6.addChangeListener(new HSlider3Change()); 
    jcomp7.addActionListener(new Button4Click());
    jcomp8.addActionListener(new Button5Click());



    //set components properties
    jcomp4.setOrientation (JSlider.HORIZONTAL);
    jcomp4.setMinorTickSpacing (10);
    jcomp4.setMajorTickSpacing (60);
    jcomp4.setPaintTicks (true);
    jcomp4.setPaintLabels (true);
    jcomp5.setOrientation (JSlider.HORIZONTAL);
    jcomp5.setMinorTickSpacing (5);
    jcomp5.setMajorTickSpacing (30);
    jcomp5.setPaintTicks (true);
    jcomp5.setPaintLabels (true);
    jcomp6.setOrientation (JSlider.HORIZONTAL);
    jcomp6.setMinorTickSpacing (5);
    jcomp6.setMajorTickSpacing (30);
    jcomp6.setPaintTicks (true);
    jcomp6.setPaintLabels (true);

    //adjust size and set layout
    setPreferredSize (new Dimension (412, 379));
    setLayout (null);

    //add components
    add (jcomp1);
    add (jcomp2);
    add (jcomp3);
    add (jcomp4);
    add (jcomp5);
    add (jcomp6);
    add (jcomp7);
    add (jcomp8);

    //set component bounds (only needed by Absolute Positioning)
    jcomp1.setBounds (5, 200, 100, 20);
    jcomp2.setBounds (155, 200, 100, 20);
    jcomp3.setBounds (295, 200, 100, 20);
    jcomp4.setBounds (5, 5, 400, 50);
    jcomp5.setBounds (5, 60, 400, 50);
    jcomp6.setBounds (5, 115, 400, 50);
    jcomp7.setBounds (5, 300, 100,20);
    jcomp8.setBounds (155,300, 100,20);
  }}
  
  //**************************************************************
//  This gets called when button is clicked
//**************************************************************

class Button1Click implements ActionListener
{
  public void actionPerformed(ActionEvent e)
  {
    JButton b = (JButton)e.getSource();
    timer=millis()+1000;
    Star1 = true;
  }
}

class Button2Click implements ActionListener
{
  public void actionPerformed(ActionEvent e)
  {
    JButton b = (JButton)e.getSource();
    timer=millis()+1000;
    Star2 = true;
  }
}


class Button3Click implements ActionListener
{
  public void actionPerformed(ActionEvent e)
  {
    JButton b = (JButton)e.getSource();
    timer=millis()+1000;
    Star3 = true;
  }
}

class Button4Click implements ActionListener
{
  public void actionPerformed(ActionEvent e)
  {
    JButton b = (JButton)e.getSource();
    timer=millis()+1000;
    Globe = true;
    Map = false;
  }
}

class Button5Click implements ActionListener
{
  public void actionPerformed(ActionEvent e)
  {
    JButton b = (JButton)e.getSource();
    timer=millis()+1000;
    Map = true;
    Globe = false;
  }
}
  
 //**************************************************************
//  This gets called when slider is changed
//**************************************************************

class HSlider1Change implements ChangeListener
{
  public void stateChanged(ChangeEvent e)
  {
    JSlider source = (JSlider)e.getSource();
         if (source.getValue() < 180) {
      GHA = radians(180 - source.getValue()); 
     }
     if (source.getValue() > 180) {
      GHA = radians(180 + (360 - source.getValue())); 
     }
     timer=millis()+1000; 
     println(GHA);
  }
}

class HSlider2Change implements ChangeListener
{
  public void stateChanged(ChangeEvent e)
  {
    JSlider source = (JSlider)e.getSource();
    alt = radians(source.getValue());
    timer=millis()+1000;
    println(degrees(alt));
  }
}

class HSlider3Change implements ChangeListener
{
  public void stateChanged(ChangeEvent e)
  {
    JSlider source = (JSlider)e.getSource();
    dec = radians(source.getValue());
//decflag = true;
    timer=millis()+1000;
    println(dec);
  }
} 
/*

 Kepler Visualization - Controls
 
 GUI controls added by Lon Riesberg, Laboratory for Atmospheric and Space Physics
 lon@ieee.org
 
 April, 2012
 
 Current release consists of a vertical slider for zoom control.  The slider can be toggled
 on/off by pressing the 'c' key.
 
 Slide out controls that map to the other key bindings is currently being implemented and
 will be released soon.
 
*/

class Controls {
   
   int barWidth;   
   int barX;                          // x-coordinate of zoom control
   int minY, maxY;                    // y-coordinate range of zoom control
   float minZoomValue, maxZoomValue;  // values that map onto zoom control
   float valuePerY;                   // zoom value of each y-pixel 
   int sliderY;                       // y-coordinate of current slider position
   float sliderValue;                 // value that corresponds to y-coordinate of slider
   int sliderWidth, sliderHeight;
   int sliderX;                       // x-coordinate of left-side slider edge                     
   
   Controls () {
      
      barX = 40;
      barWidth = 15;
 
      minY = 40;
      maxY = minY + height/3 - sliderHeight/2;
           
      minZoomValue = height - height;
      maxZoomValue = height;   // 300 percent
      valuePerY = (maxZoomValue - minZoomValue) / (maxY - minY);
      
      sliderWidth = 25;
      sliderHeight = 10;
      sliderX = (barX + (barWidth/2)) - (sliderWidth/2);      
      sliderValue = minZoomValue; 
      sliderY = minY;     
   }
   
   
   public void render() {

     // strokeWeight(1.5); 
        strokeWeight(1); 
    //  stroke(105, 105, 105);  // fill(0xff33ff99);
   //   stroke(0xff33ff99);  // fill(0xff33ff99);  0xffff0000
       stroke(0xffff0000);
      
      // zoom control bar
      fill(0, 0, 0, 0);
        
      rect(barX, minY, barWidth, maxY-minY);
      
      // slider
     // fill(105, 105, 105); //0x3300FF00
       fill(0xffff0000); // 0xff33ff99//0x3300FF00
      rect(sliderX, sliderY, sliderWidth, sliderHeight);
   }
   
   
   public float getZoomValue(int y) {
      if ((y >= minY) && (y <= (maxY - sliderHeight/2))) {
         sliderY = (int) (y - (sliderHeight/2));     
         if (sliderY < minY) { 
            sliderY = minY; 
         } 
         sliderValue = (y - minY) * valuePerY + minZoomValue;
      }     
      return sliderValue;
   }
   
   
   public void updateZoomSlider(float value) {
      int tempY = (int) (value / valuePerY) + minY;
      if ((tempY >= minY) && (tempY <= (maxY-sliderHeight))) {
         sliderValue = value;
         sliderY = tempY;
      }
   }
   
   
   public boolean isZoomSliderEvent(int x, int y) {
      int slop = 50;  // number of pixels above or below slider that's acceptable.  provided for ease of use.
      int sliderTop = (int) (sliderY - (sliderHeight/2)) - slop;
      int sliderBottom = sliderY + sliderHeight + slop;
      return ((x >= sliderX) && (x <= (sliderX    + sliderWidth)) && (y >= sliderTop)  && (y <= sliderBottom) || draggingZoomSlider );
   } 
}
 
/*
I modified this so the slider is horizontal.  That gives me a vertical for
tweaking altitude and horizontal for right ascension/longitude
*/

/*

 Kepler Visualization - Controls
 
 GUI controls added by Lon Riesberg, Laboratory for Atmospheric and Space Physics
 lon@ieee.org
 
 April, 2012
 
 Current release consists of a vertical slider for zoom control.  The slider can be toggled
 on/off by pressing the 'c' key.
 
 Slide out controls that map to the other key bindings is currently being implemented and
 will be released soon.
 
*/

class HorizontalControl {
   
   int barHeight;   
   int barY;                          // y-coordinate of zoom control
   int minX, maxX;                    // x-coordinate range of zoom control
   float minZoomValue, maxZoomValue;  // values that map onto zoom control
   float valuePerX;                   // zoom value of each y-pixel 
   int sliderY;                       // y-coordinate of current slider position
   float sliderValue;                 // value that corresponds to y-coordinate of slider
   int sliderWidth, sliderHeight;
   int sliderX;                       // x-coordinate of left-side slider edge                     
   
   HorizontalControl () {
      
      barY = 15; //40;
      barHeight = 40; //15;
 
      minX = 40;
      maxX = minX + width/3 - sliderWidth/2;
           
      minZoomValue = texture.width - texture.width;
      maxZoomValue = texture.width;   // 300 percent
      valuePerX = (maxZoomValue - minZoomValue) / (maxX - minX);
      
      sliderWidth = 10; //25;
      sliderHeight = 25; //10;
     // sliderY = (barY + (barHeight/2)) - (sliderHeight/2);
      sliderY = (barY - (sliderHeight/2)) + (barHeight/2);
      sliderValue = minZoomValue; 
      sliderX = minX;     
   }
   
   
   public void render() {
       pushMatrix();


     // strokeWeight(1.5); 
        strokeWeight(1); 
    //  stroke(105, 105, 105);  // fill(0xff33ff99);
   //   stroke(0xff33ff99);  // fill(0xff33ff99);  0xffff0000
       stroke(0xffff0000);
      
      // zoom control bar
      fill(0, 0, 0, 0);
        
      rect(minX,barHeight + height - height/4,maxX-minX, barY );
     // rect(maxX-minX, barHeight/2,minX,barY + height - height/4 );
      
      // slider
     // fill(105, 105, 105); //0x3300FF00
       fill(0xffff0000); // 0xff33ff99//0x3300FF00

      rect(sliderX, sliderY + height - height/4 + sliderHeight/2 , sliderWidth, sliderHeight);

      popMatrix();
      
   }
   
   
   public float getZoomValue(int x, int y) {
      if ((x >= minX) && (x <= (maxX - sliderWidth/2)) && (y > (height - height/3))) {
         sliderX = (int) (x - (sliderWidth/2));     
         if (sliderX < minX) { 
            sliderX = minX; 
         } 
         sliderValue = (x - minX) * valuePerX + minZoomValue;
      }     
      return sliderValue;
   }
   
   
   public void updateZoomSlider(float value) {
      int tempX = (int) (value / valuePerX) + minX;
      
      if ( (tempX >= minX) && (tempX <= (maxX+sliderWidth))  ) {
         sliderValue = value;
         sliderX = tempX;
      }
   }
   
   
/*   boolean isZoomSliderEvent(int x, int y) {
      int slop = 50;  // number of pixels above or below slider that's acceptable.  provided for ease of use.
      int sliderTop = (int) (sliderY - (sliderHeight/2)) - slop;
      int sliderBottom = sliderY + sliderHeight + slop;
      return ((x >= sliderX) && (x <= (sliderX    + sliderWidth)) && (y >= sliderTop)  && (y <= sliderBottom) || draggingZoomSlider );
   } */
   
      public boolean isZoomSliderEvent(int x, int y) {
      int slop = 50;  // number of pixels above or below slider that's acceptable.  provided for ease of use.
      int sliderLeft = (int) (sliderX - (sliderWidth/2)) - slop;
      int sliderRight = sliderX + sliderWidth + slop;
    //  return ((y >= sliderY + height - height/4) && (y <= (sliderY + height - height/4    + sliderHeight)) && (x >= sliderLeft)  && (x <= sliderRight) || draggingZoomSlider );
           return ((y >= sliderY + height - height/4 - sliderHeight/2) && (y <= (sliderY + height - height/4 + sliderHeight*2 )) && (x >= sliderLeft )  && (x <= sliderRight ) || draggingZoomSlider );
   } 
}
public boolean getPoints(){

  
        texture.beginDraw();
        int w = 0;

        for( double L0 = -180.0f; L0 <= 180.0f; L0 += .1f )
        {
           // Mz = Rz(Math.toRadians(360.0) - (GHA + Math.toRadians(36.082)), Mz);
            Mz = Rz(Math.toRadians(360.0f) - (GHA1 ), Mz);


            My =  Ry(Math.toRadians(90.0f) - (-dec1), My);
    released = false;
         //   float alt = map(mouseX,width-width,width,0,90);
              vv =  VectorSpherical2Cartesian((-alt1),Math.toRadians(L0) );
         //   vv =  VectorSpherical2Cartesian(alt,Math.toRadians(L0) );


            vy =  MatrixVecProd( My, vv, vy );

            vyz =  MatrixVecProd( Mz, vy, vyz );



            wpt[w] = C2ELat( vyz[0], vyz[1], vyz[2]);
            wpt[w+1] = C2ELon( vyz[0], vyz[1], vyz[2]);

            WPT = toFloatArray(wpt);

            x[w] = map(WPT[w+1],radians(-180) ,radians(180),texture.width, texture.width - texture.width);
            y[w] = map(WPT[w],radians(-90),(radians(90)),texture.height,texture.height - texture.height);
            texture.point(x[w],y[w]);
            texture.noFill();
            texture.stroke(255,0, 0);
            texture.strokeWeight(2);
            texture.beginShape();
            texture.curveVertex(x[w],y[w]);
            texture.curveVertex(x[w], y[w]);

            texture.endShape();
          
        }
w++;
        w = 0;
        for( double L0 = -180.0f; L0 <= 180.0f; L0 += .1f )
        {
          //  Mz = Rz(Math.toRadians(360.0) - (GHA1 + Math.toRadians(36.3334) ), Mz);
            Mz = Rz(Math.toRadians(360.0f) - (GHA2 ), Mz);


            My =  Ry((Math.toRadians(90.0f) - (-dec2)), My);
            //   float alt = map(mouseX,width-width,width,0,90);
            vv =  VectorSpherical2Cartesian((-alt2),Math.toRadians(L0) );
            //   vv =  VectorSpherical2Cartesian(alt,Math.toRadians(L0) );


            vy =  MatrixVecProd( My, vv, vy );

            vyz =  MatrixVecProd( Mz, vy, vyz );



            wpt[w] = C2ELat( vyz[0], vyz[1], vyz[2]);
            wpt[w+1] = C2ELon( vyz[0], vyz[1], vyz[2]);

            WPT = toFloatArray(wpt);

            x[w] = map(WPT[w+1],radians(-180) ,radians(180),texture.width, texture.width - texture.width);
            y[w] = map(WPT[w],radians(-90),(radians(90)),texture.height,texture.height - texture.height);
            texture.point(x[w],y[w]);
            texture.noFill();
            texture.stroke(255,255, 255);
            texture.strokeWeight(2);
            texture.beginShape();
            texture.curveVertex(x[w],y[w]);
            texture.curveVertex(x[w], y[w]);

            texture.endShape();
        }
        w++;
w = 0;
        for( double L0 = -180.0f; L0 <= 180.0f; L0 += .1f )
        {
           // Mz = Rz(Math.toRadians(360.0) - (GHA2 + Math.toRadians(36.442675)), Mz);
            Mz = Rz(Math.toRadians(360.0f) - (GHA3), Mz);


            My =  Ry(Math.toRadians(90.0f) - (-dec3), My);
            //   float alt = map(mouseX,width-width,width,0,90);
            vv =  VectorSpherical2Cartesian((-alt3),Math.toRadians(L0) );
            //   vv =  VectorSpherical2Cartesian(alt,Math.toRadians(L0) );


            vy =  MatrixVecProd( My, vv, vy );

            vyz =  MatrixVecProd( Mz, vy, vyz );



            wpt[w] = C2ELat( vyz[0], vyz[1], vyz[2]);
            wpt[w+1] = C2ELon( vyz[0], vyz[1], vyz[2]);

            WPT = toFloatArray(wpt);

            x[w] = map(WPT[w+1],radians(-180) ,radians(180),texture.width, texture.width - texture.width);
            y[w] = map(WPT[w],radians(-90),(radians(90)),texture.height,texture.height - texture.height);
            texture.point(x[w],y[w]);
            texture.noFill();
            texture.stroke(0,0, 255);
            texture.strokeWeight(2);
            texture.beginShape();
            texture.curveVertex(x[w],y[w]);
            texture.curveVertex(x[w], y[w]);

            texture.endShape();
        }
        w++;

        
        texture.endDraw();
released = true;
   return released; 
         
    }
 // public void settings() {  size(1275, 750, P3D);  smooth(); }
//}

2 Likes

…And here’s a screenshot.

3 Likes

Thanks for sharing this, Harry! Is the “position fix” in your demo image the point of intersection just west of Florida?

Yes, I didnt make that clear that a position fix is when 3 circles intersect. Right now I’m revisiting Unfolding for Maps in processing-2.2.1 . This would allow to zoom in and see where the “fix” is on a Microsoft satellite map. The biggest limit on accuracy right now is how the variables like altitude are input - you can only get somewhat close with those sliders.

1 Like

I got this to work with Unfolding in processing-2.2.1. I used Interactive Computer Ephemeris from USNO to calculate for 39.21N, -77.21W and used Dubhe, Arcturus, Betelgeuse. I entered the GHA,dec,alt for each by writing it into the code instead of using the sliders, to see how well my code works with Unfolding and it works fairly nicely, except the more you zoom the points dont show well. When I use the sliders, there is mlles/kilometers of slop. Here’s a shot of a “fix” in Unfolding.

2 Likes

Looks great!!

Maybe use the sliders to update values in value boxes – but also allow the user to edit the values directly? Then you can either 1) type them in or 2) slide them, roughly, or 3) rough-them in with the sliders, then fine-tune by editing.

1 Like

Yeah, good idea about the input. I’ll need to research it because I really only know what others have already written, i.e., I can arrange but Im still lousy at original work. Anyway, changing the input method shuldnt be too hard, just need some time.

Im getting somewhere now. I need to figure out how to set a slider value based on text input and text based on slider value. I have no idea how to do that and suspect it might even discouraged. It would be cool though. The current layout for swing components is just to get them in and not optimal. I’ll have to rearrange them so GHA slider is near the GHA text input, etc.

Any chance that you can link or upload the “earthmap1.jpg” file?

I didnt link it because I couldnt remember where I found it, so I went searching and I got it by downloading JPARSEC from this site: http://conga.oan.es/~alonso/doku.php?id=jparsec To find the earthmap1.jpg file you have to look inside jprarsec.jar and look for textures.jar, it is inside that.

BTW, JPARSEC is very cool and is written in java. I’ve used some of it in a few sketches.

Here’s the link to jparsec/lib which contains textures.jar

http://conga.oan.es/~alonso/jparsec/lib/

1 Like

I found an example of java swing that combines text input with a slider. When you change the slider, the text changes and if you change the text directly, the slider updates. I was able to stick it directly into my code by adding a tab and then instantiating that class(I think that’s what its called when you use a class). There is one big problem though and that is that it only works with integers, but I have an idea. I found another example that also ties text/slider together and it uses Double.

Here’s the link to the integer example:

https://bitbucket.org/prubin/comboslider/src/master/src/comboslider/ComboSlider.java

1 Like

Not sure if it would be useful, but I recently saw a new Processing UI library you might take a look at:

1 Like

Odd, I don’t see any textures.jar inside that jparsec.jar download.

Sorry about the link, jparsec is cool but the site is a pain to read. I went to this link and textures.jar is listed.

http://conga.oan.es/~alonso/jparsec/lib/

1 Like
earthmap1.jpg

That looks good. It looks like its built on swing and sliders in swing use int, so I will be in the same spot trying to input text that represents a double and simultaneously update a slider that needs an int.

I modified that ComboSlider example so that it displays a double value, but when I try to edit the textfield it mixes the order of the numbers. That’s not useful. I mentioned a double slider example but it wont allow to edit the textfield either. I can change one digit after the decimal point but if I try to change more, it jumps to min or max.

1 Like

I reduced my sketch down to just a few swing inputs like one slider, one button, one textfield and gave it another try. I almost have it. I can move the slider and the textfield changes and shows a double. I can also edit the textfield. Edit: Whoops, now I can do both if I use a button to do it. Press the button after entering text and the slider updates.
Edit: Forgot to update the code. And, to use it, enter the text by typing and hitting enter. Then hit the GHA button. Or, move the slider and then hit enter in the textfield or it causes a nullpointer. Also, if you hit GHA and it prints a value in radians, then hit GHA again. Its not perfect, but it is a start.

Here’s the reduced code:

//Generated by GuiGenie - Copyright (c) 2004 Mario Awad.
//Home Page http://guigenie.cjb.net - Check often for new versions!
//import java.awt.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.text.DecimalFormat;

MyPanel controlPanel;
public String text;


double GHA = Math.toRadians(-51.16667f); //(-128.83333f);
double GHA1 = Math.toRadians(51.16667f);

//double alt = Math.toRadians(62.6f);

int timer=0;

boolean Star1;



public class MyPanel extends JPanel {
    private TextDemo jcomp1;
    private JLabel jcomp2;
    private JButton jcomp3;

    //private JButton jcomp9;
    private JButton jcomp4;
    private JSlider jcomp5;


    public MyPanel() {
        //construct components
        jcomp1 = new TextDemo(); //JTextArea (1, 16);
        jcomp2 = new JLabel ("GHA : DDD.MMSS");
        jcomp3 = new JButton ("GHA");
        jcomp4 = new JButton("Star1");
        jcomp5 = new JSlider(0,360,(int)(Math.toDegrees(-GHA)));  //,(int)Math.toDegrees(GHA));

        jcomp3.addActionListener(new Button1Click());
        jcomp4.addActionListener(new Button2Click());
        jcomp5.addChangeListener(new HSlider1Change());
        
        jcomp5.setOrientation (JSlider.HORIZONTAL);
        jcomp5.setMinorTickSpacing (10);
        jcomp5.setMajorTickSpacing (60);
        jcomp5.setPaintTicks (true);
        jcomp5.setPaintLabels (true);
               
        //adjust size and set layout
        setPreferredSize (new Dimension (544, 756));
        setLayout (null);

        //add components
        add (jcomp1);
        add (jcomp2);
        add (jcomp3);
        add (jcomp4);
        add (jcomp5);


        //set component bounds (only needed by Absolute Positioning)
        jcomp1.setBounds (70, 35, 205, 25);
        jcomp2.setBounds (285, 35, 175, 25);
        jcomp3.setBounds (70, 80, 100, 25);
        jcomp4.setBounds (70,320,100,25);
        jcomp5.setBounds(70,420,400,50);
        
 }
 
    
class Button1Click implements ActionListener
{
  public void actionPerformed(ActionEvent e)
  {
    JButton b = (JButton)e.getSource();
    GHA = Double.parseDouble(text);
            jcomp5.setValue((int)Double.parseDouble(text));
    jcomp1.textField.setText(text);

    println("GHA: " + GHA);
    timer=millis()+1000;

  }
}


class Button2Click implements ActionListener
{
  public void actionPerformed(ActionEvent e)
  {
    JButton b = (JButton)e.getSource();

    GHA1 = GHA;
    println("GHA1: " + GHA1); 
    //  dec1 = dec;
    //  alt1 = alt;
    // Globe = true;
    //  Map = false;
    Star1 = true;
    timer=millis()+1000;
  }
}


class HSlider1Change implements ChangeListener
{
          
  public void stateChanged(ChangeEvent e)
  {
    JSlider source = (JSlider)e.getSource();
     if (source.getValue() < 180){
        GHA = radians( (- source.getValue()));
        jcomp1.textField.setText(Double.toString(Math.toDegrees(-GHA))); 
     }
     if (source.getValue() > 180) {
        GHA = radians((360 - source.getValue())); 
        jcomp1.textField.setText(Double.toString(360-(Math.toDegrees(GHA))));
     }
     timer=millis()+1000; 
 
     println("slider: " + source.getValue() + " GHA: " + Math.toDegrees(GHA));
  }
}


}


void setup()
{

  JFrame frame =new JFrame("Controls");
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  controlPanel = new MyPanel();
  controlPanel.setOpaque(true); //content panes must be opaque
  frame.setContentPane(controlPanel);
//frame.add(new TextDemo());
  //Display the window.
  frame.pack();
  frame.setVisible(true);
}




public class TextDemo extends JPanel implements ActionListener {
    protected JTextField textField;
    protected JTextArea textArea;
    private final static String newline = "\n";
 
    public TextDemo() {

        textField = new JTextField(20);
        textField.addActionListener(this);
        textField.setHorizontalAlignment(textField.CENTER);
        textArea = new JTextArea(2, 20);
        textArea.setEditable(false);

        add(textField);
        add(textArea);
    }
 
    public void actionPerformed(ActionEvent evt) {

        text = textField.getText();
        textArea.append(text + newline);
        textField.selectAll();
        GHA = Double.parseDouble(text);
        textField.setText(Double.toString(GHA));
        
        
        println(GHA);
 
        //Make sure the new text is visible, even if there
        //was a selection in the text area.

    }
}

1 Like

I tried to implement the changes I made in the stripped down GUI code. It worked mostly, except updating the slider caused some problems, so I commented that out. I kept the parts that updated the text and then calculated a fix using the Interactive Computer Ephemeris, and input the data by editing the text. I’m posting this shot because it shows good geometry. If I get the GUI straight, I want to use all the points to calculate an actual intersection. Its good that I can zoom in but the points get lost, the closer you zoom.

1 Like

I stumbled onto a nice feature of the points generating code. If you enter the declination as 90.00 degrees and the altitude as some latitude, then the points generated will draw a line of latitude. It makes sense. If polaris which is basically declination 90.00 degrees is observed at your latitude, the observed altitude of polaris will be your latitude.

I’ve been reading about the history of celestial navigation and got to the part about Sumner and lines of position and using the transit of a body across the local meridian as a way to check chronometers. So, I used ICE to calculate the transit of the sun. I then picked 1 hour before transit, time of transit and 1 hour after. I got three circles of position, with transit being the smallest. I thought it was interesting that it shows that the sun at local noon(transit)is at the highest altitude and the two circles preceding and following have equal altitudes and are bigger than transit because the alttiude is lower. It just illustrates what these circles are in a clear way.

Finally, just out of curiosity if I can draw a latitude line, can I also draw a longitude line. If I subtract my longitude from 90 degrees and set declination to 0 degrees and altitude also to 0 degrees, then yes.

1 Like

Alright, enough of that. Now I need to write an algorithm that uses the points generated to calculate an intersection. What I have now is 3600 points calculated for each circle, put into an array, then drawn on the map. It looks good, but the intersection is visual only.