import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class FloatingRadius extends Applet implements ActionListener, ItemListener { private boolean finished_parsing_text; private boolean formatting_OK; private boolean sign_allready_specified; private static final int ADAM = 1; private static final int EDWARD = 2; private static final int DEGREE_FIELD = 0; private static final int MINUTE_FIELD = 1; private static final int SECOND_FIELD = 2; private static final int DECIMAL_DEGREES = 0; private static final int DECIMAL_MINUTES = 1; private static final int DECIMAL_SECONDS = 2; private static final int STATUTE_MILES = 0; private static final int KILOMETERS = 1; private static final int NAUTICAL_MILES = 2; private static final double ERROR_DOUBLE = -666.0d; private static final double ZERO = 0.0d; private static final double ONE = 1.0d; private static final double NINETY = 90.0d; private static final double[] max_allowed_values = {180.0d, 60.0d, 60.0d}; private static final double[] ratios_to_degree = { 1.0, 60.0d, 3600.0d}; private static final double MINUTES_PER_DEGREE = 60.0d; private static final double SECONDS_PER_DEGREE = 3600.0d; private static final double DEG_TO_RAD_CONV_FACTOR = (double)3.141592653589732 / (double)180.0; private static final double MEAN_TERRESTRIAL_RADIUS_IN_STATUTE_MILES = 3958.909d; private static final double EQUATORIAL_TERRESTRIAL_RADIUS_IN_KILOMETERS = 6378.16d; private static final double POLAR_TERRESTRIAL_RADIUS_IN_KILOMETERS = 6356.77d; private static final double STATUTE_MILES_TO_KILOMETERS_CONV_FACTOR = 1.609344d; private static final double STATUTE_MILES_TO_NAUTICAL_M_CONV_FACTOR = 1.0d / 1.1516d; private static final double KILOMETERS_TO_NAUTICAL_M_CONV_FACTOR = 1.0d / (1.1516d * 1.609344d); private static final double MEAN_TERRESTRIAL_RADIUS_IN_KILOMETERS = STATUTE_MILES_TO_KILOMETERS_CONV_FACTOR * MEAN_TERRESTRIAL_RADIUS_IN_STATUTE_MILES; private static final double MEAN_TERRESTRIAL_RADIUS_IN_NAUTICAL_MILES = STATUTE_MILES_TO_NAUTICAL_M_CONV_FACTOR * MEAN_TERRESTRIAL_RADIUS_IN_STATUTE_MILES; private static final String DEFAULT_FORMATTING_ERROR_MESSAGE = "input error"; private static final String[] default_reset_coordinates = {"40 N", "105 W", "33 N", "117 W", "42 N", "113 W"}; private int c_char_index, c_sign; private int i_char, i_field, i; private int i_panel, i_text; private int str_len, temp_int, text_index; private int output_unit_type = STATUTE_MILES; private int type_of_theory = ADAM; private float rounded_reported_radius; private float rounded_reported_latitude; private float rounded_reported_longitude; private double c_coordinate, c_double; private double c_latitude, c_longitude; private double cosine_radius; private double radian_radius; private double reported_radius; private double temp; private Double val; private char c_char, c_upper_case_char; private char double_val[]; private char[] field_chars = {'\u00b0', '\'', '\"'}; private Character chr = new Character('0'); private String str, str_as_double; Panel control_panel = new Panel(); Panel lat_lon_label_panel = new Panel(); Panel[] input_value_panel = new Panel[3]; Panel radio_panel = new Panel(); Panel action_panel = new Panel(); Panel[] output_value_panel = new Panel[2]; Label ghost_lat_label = new Label(" ", Label.CENTER); Label lat_label = new Label("Latitude ", Label.CENTER); Label lon_label = new Label("Longitude", Label.CENTER); Label ghost_lon_label = new Label(" ", Label.CENTER); Label point_label_1 = new Label("point 1 -", Label.LEFT); Label point_label_2 = new Label("point 2 -", Label.LEFT); Label point_label_3 = new Label("point 3 -", Label.LEFT); TextField[] input_text = new TextField[6]; CheckboxGroup output_units_option = new CheckboxGroup(); Checkbox statute_miles = new Checkbox("statute miles", output_units_option, true); Checkbox kilometers = new Checkbox("kilometers", output_units_option, false); Checkbox nautical_miles = new Checkbox("nautical miles", output_units_option, false); private Button reset_1 = new Button("reset 1"); private Button reset_2 = new Button("reset 2"); private Button reset_3 = new Button("reset 3"); private Button compute = new Button("find radius and center"); private TextField output_text_radius = new TextField(" 0.0", 9); private TextField output_text_center = new TextField(" 0 N; 0 W", 18); private Font control_panel_font = new Font( "TimesRoman", Font.BOLD, 14); private Font mid_control_panel_font = new Font( "TimesRoman", Font.BOLD, 18); private Font large_control_panel_font = new Font( "TimesRoman", Font.BOLD, 24); private Font giant_control_panel_font = new Font( "TimesRoman", Font.BOLD, 36); Color MyGreen = new Color(0, 192, 0); Color MyCyan = new Color(0, 192, 128); Color NonEditableBkgd = new Color(64, 128, 64); private TeraPoint c_point; private TeraPoint center; private TeraPoint[] point = new TeraPoint[3]; public void init() { setBackground(Color.white); setLabels(); setTextFields(); setButtons(); addListeners(); definePanelStructuresAndAddLayouts(); setTextFieldDefaultEditStatuses(); setLayout(new BorderLayout()); add("North", control_panel); } // end init method public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command == "find radius and center") { if (!getAndsetTextfieldParameters()) { output_text_radius.setText(DEFAULT_FORMATTING_ERROR_MESSAGE); return; } convertToRadianMeasure(); computeCartesianCoordinates(); computeCenterAndRadius(); // note the symmetry about this central method computeSphericalCoordinates(); convertToDegreeMeasure(); convertToOutputUnitsAndDisplayResults(); } else if (command == "reset 1") { resetFields(0); } else if (command == "reset 2") { resetFields(1); } else if (command == "reset 3") { resetFields(2); }; } // end actionPerformed method public void itemStateChanged(ItemEvent event) { String command = (String) event.getItem(); if (command == "statute miles") { output_unit_type = STATUTE_MILES; }; if (command == "kilometers") { output_unit_type = KILOMETERS; }; if (command == "nautical miles") { output_unit_type = NAUTICAL_MILES; }; convertToOutputUnitsAndDisplayDistance(); } // end method itemStateChanged private void setLabels() { lat_label.setFont(mid_control_panel_font); lat_label.setForeground(Color.red); lon_label.setFont(mid_control_panel_font); lon_label.setForeground(Color.red); point_label_1.setFont(mid_control_panel_font); point_label_1.setForeground(Color.blue); point_label_2.setFont(mid_control_panel_font); point_label_2.setForeground(Color.blue); point_label_3.setFont(mid_control_panel_font); point_label_3.setForeground(Color.blue); statute_miles.setFont(control_panel_font); statute_miles.setForeground(Color.magenta); kilometers.setFont(control_panel_font); kilometers.setForeground(MyCyan); nautical_miles.setFont(control_panel_font); nautical_miles.setForeground(MyCyan); } private void setTextFields() { for (i_text = 0; i_text < 6; i_text++) { input_text[i_text] = new TextField(); input_text[i_text].setFont(control_panel_font); input_text[i_text].setText(default_reset_coordinates[i_text]); input_text[i_text].setColumns(18); } output_text_radius.setFont(large_control_panel_font); output_text_center.setFont( mid_control_panel_font); } private void setButtons() { reset_1.setBackground(Color.blue); reset_2.setBackground(Color.blue); reset_3.setBackground(Color.blue); reset_1.setForeground(Color.yellow); reset_2.setForeground(Color.yellow); reset_3.setForeground(Color.yellow); reset_1.setFont(mid_control_panel_font); reset_2.setFont(mid_control_panel_font); reset_3.setFont(mid_control_panel_font); compute.setBackground(Color.green); compute.setForeground(Color.red); compute.setFont(large_control_panel_font); } private void addListeners() { statute_miles.addItemListener(this); kilometers.addItemListener(this); nautical_miles.addItemListener(this); reset_1.addActionListener(this); reset_2.addActionListener(this); reset_3.addActionListener(this); compute.addActionListener(this); } private void definePanelStructuresAndAddLayouts() { GridLayout control_panel_layout = new GridLayout(8, 1, 0, 20); // determine vertical number later control_panel.setLayout(control_panel_layout); FlowLayout f = new FlowLayout(FlowLayout.LEFT, 0, 0); FlowLayout f_cen = new FlowLayout(FlowLayout.CENTER, 40, 0); GridLayout grid_panel_layout_4 = new GridLayout(1, 4, 0, 0); /// lat/long label panel - Panel[] flow_lat_lon_label_panel = new Panel[4]; for (i_panel = 0; i_panel < 4; i_panel++) { flow_lat_lon_label_panel[i_panel] = new Panel(); flow_lat_lon_label_panel[i_panel].setLayout(f); } flow_lat_lon_label_panel[0].add(ghost_lat_label); flow_lat_lon_label_panel[1].add(lat_label); flow_lat_lon_label_panel[2].add(lon_label); flow_lat_lon_label_panel[3].add(ghost_lon_label); lat_lon_label_panel.setLayout(grid_panel_layout_4); for (i_panel = 0; i_panel < 4; i_panel++) lat_lon_label_panel.add(flow_lat_lon_label_panel[i_panel]); control_panel.add(lat_lon_label_panel); /// for input of lat/long values and point labels - for (i_panel = 0; i_panel < 3; i_panel++) input_value_panel[i_panel] = new Panel(); Panel[] flow_input_value_panel = new Panel[12]; for (i_panel = 0; i_panel < 12; i_panel++) { flow_input_value_panel[i_panel] = new Panel(); flow_input_value_panel[i_panel].setLayout(f); } // point 1 ... flow_input_value_panel[0].add(point_label_1); flow_input_value_panel[1].add(input_text[0]); flow_input_value_panel[2].add(input_text[1]); flow_input_value_panel[3].add(reset_1); input_value_panel[0].setLayout(grid_panel_layout_4); for (i_panel = 0; i_panel < 4; i_panel++) input_value_panel[0].add(flow_input_value_panel[i_panel]); control_panel.add(input_value_panel[0]); // point 2 ... flow_input_value_panel[4].add(point_label_2); flow_input_value_panel[5].add(input_text[2]); flow_input_value_panel[6].add(input_text[3]); flow_input_value_panel[7].add(reset_2); input_value_panel[1].setLayout(grid_panel_layout_4); for (i_panel = 4; i_panel < 8; i_panel++) input_value_panel[1].add(flow_input_value_panel[i_panel]); control_panel.add(input_value_panel[1]); // point 3 ... flow_input_value_panel[ 8].add(point_label_3); flow_input_value_panel[ 9].add(input_text[4]); flow_input_value_panel[10].add(input_text[5]); flow_input_value_panel[11].add(reset_3); input_value_panel[2].setLayout(grid_panel_layout_4); for (i_panel = 8; i_panel < 12; i_panel++) input_value_panel[2].add(flow_input_value_panel[i_panel]); control_panel.add(input_value_panel[2]); /// for input of lat/long values end radio_panel.setLayout(f_cen); radio_panel.add(statute_miles); radio_panel.add(kilometers); radio_panel.add(nautical_miles); control_panel.add(radio_panel); for (i_panel = 0; i_panel < 2; i_panel++) output_value_panel[i_panel] = new Panel(); output_value_panel[0].setLayout(f_cen); output_value_panel[0].add(output_text_radius); control_panel.add(output_value_panel[0]); FlowLayout action_panel_layout = new FlowLayout(FlowLayout.CENTER, 60, 0); action_panel.setLayout(action_panel_layout); action_panel.add(compute); control_panel.add(action_panel); output_value_panel[1].setLayout(f_cen); output_value_panel[1].add(output_text_center); control_panel.add(output_value_panel[1]); } private void setTextFieldDefaultEditStatuses() { for (i = 0; i < 6; i++) { input_text[i].setBackground(Color.white); input_text[i].setEditable(true); } } private void resetFields(int point_id) { text_index = 2*point_id; input_text[text_index].setText(default_reset_coordinates[text_index]); text_index++; input_text[text_index].setText(default_reset_coordinates[text_index]); reported_radius = radian_radius = ZERO; output_text_radius.setText(" 0.0"); // later may place here a reset of the center coordinates // output_text_center.setText(" 0 N; 0 W"); } private boolean getAndsetTextfieldParameters() { formatting_OK = true; getAndsetEndpointParameters(0); getAndsetEndpointParameters(1); getAndsetEndpointParameters(2); return formatting_OK; } private void getAndsetEndpointParameters(int point_id) { c_latitude = getDoubleFromString(input_text[2*point_id] ); c_longitude = getDoubleFromString(input_text[2*point_id+1]); // instantiate the endpoint class object point[point_id] = new TeraPoint(c_latitude, c_longitude); } private double getDoubleFromString(TextField text) { str = text.getText(); str_len = str.length(); upFrontConsiderations(); for (i_field = DEGREE_FIELD; i_field <= SECOND_FIELD; i_field++) { if (!parseNextStringFragment(i_field)) { formatting_OK = false; return ERROR_DOUBLE; } c_coordinate += c_double / ratios_to_degree[i_field]; if (finished_parsing_text) break; } return c_coordinate; } private void upFrontConsiderations() { c_coordinate = ZERO; c_char_index = 0; sign_allready_specified = false; while (chr.isSpaceChar(str.charAt(c_char_index))) ++c_char_index; // skip white space c_char = str.charAt(c_char_index); c_sign = 1; if (c_char == '-') { c_sign = -1; c_char_index++; } // change sign if needed else if (c_char == '+') { c_char_index++; } else { c_upper_case_char = chr.toUpperCase(c_char); if (checkIfNEWScharacter(c_upper_case_char)) { sign_allready_specified = true; if (IsASignInversionCharacter(c_upper_case_char)) c_sign = -1; // Invert sign if needed. c_char_index++; } // end if the character was NEWS whence increment pointer } // c_char_index is NOT incremented if not a NEWS character (or -, +) since it // is then likely to be at the first digit of the degree field. while (chr.isSpaceChar(str.charAt(c_char_index))) ++c_char_index; // skip white space AFTER any possible - or + sign finished_parsing_text = false; } // end method upFrontConsiderations private boolean parseNextStringFragment(int field_type) { if (!getDoubleFromStringFragment(c_char_index)) return false; // ^ serves role of C-language strtod() // ^ retrieves the next value, if any if ((c_double < ZERO) || (c_double > max_allowed_values[field_type])) return false; c_double *= c_sign; if (c_char_index == str_len) return finished_parsing_text = true; for (i_char = c_char_index; i_char < str_len; i_char++) { if (chr.isSpaceChar(str.charAt(i_char))) ++c_char_index; // skip white space else break; } if (c_char_index == str_len) { return finished_parsing_text = true; } // this is the last field c_char = str.charAt(c_char_index); if (c_char == field_chars[field_type] || c_char == ':') { ++c_char_index; for (i_char = c_char_index; i_char < str_len; i_char++) { if (chr.isSpaceChar(str.charAt(i_char))) ++c_char_index; // skip white space else break; } } // skip degree sign or colon if (c_char_index == str_len) { return finished_parsing_text = true; } // this is the last field c_char = str.charAt(c_char_index); c_upper_case_char = chr.toUpperCase(c_char); if (checkIfNEWScharacter(c_upper_case_char)) { if (!NEWSconsiderations()) return false; return finished_parsing_text = true; } // end if next character was NEWS. Sign must be positive or format error. if (Math.round(c_double) != c_double) return false; // at this point have another field so be certain the current field is integal-valued return true; } // end method parseNextStringFragment private boolean getDoubleFromStringFragment(int index_start) { int index_end, double_val_str_len; for (i_char = index_start; i_char < str_len; i_char++) { if (!checkIfPartOfRealNumber(str.charAt(i_char))) break; c_char_index++; } index_end = c_char_index; double_val_str_len = index_end - index_start; if (double_val_str_len == 0) return false; double_val = new char[double_val_str_len]; for (i_char = index_start; i_char < index_end; i_char++) double_val[i_char-index_start] = str.charAt(i_char); str_as_double = new String(double_val, 0, double_val_str_len); c_double = (Double.valueOf(str_as_double)).doubleValue(); return true; } // end method getDoubleFromStringFragment private boolean checkIfPartOfRealNumber(char ch) { return ((chr.isDigit(ch) || (ch == '.'))); } private boolean checkIfNEWScharacter(char ch) { return ((ch == 'N') || (ch == 'E') || (ch == 'W') || (ch == 'S')); } private boolean IsASignInversionCharacter(char ch) { return ((ch == 'S') || (ch == 'W')); } private boolean NEWSconsiderations() { if (sign_allready_specified) return false; // allready had a NEWS character specified up front if (c_sign < 0) return false; // allready had a "-" sign up front : error exit if (IsASignInversionCharacter(c_upper_case_char)) { c_coordinate *= -1; c_double *= -1; } ++c_char_index; for (i_char = c_char_index; i_char < str_len; i_char++) { if (!chr.isSpaceChar(str.charAt(i_char))) return false; // make sure there is no non-white text remaining } return true; } private void convertToRadianMeasure() { for (i = 0; i < 3; i++) { point[i].co_lat_rad = DEG_TO_RAD_CONV_FACTOR * point[i].co_lat; point[i].longit_rad = DEG_TO_RAD_CONV_FACTOR * point[i].longitude; } } private void computeCartesianCoordinates() { for (i = 0; i < 3; i++) { c_point = point[i]; temp = Math.sin(c_point.co_lat_rad); c_point.x = temp * Math.cos(c_point.longit_rad); c_point.y = temp * Math.sin(c_point.longit_rad); c_point.z = Math.cos(c_point.co_lat_rad); } } private void computeSphericalCoordinates() { center.longit_rad = Math.atan2(center.y, center.x); center.co_lat_rad = Math.acos(center.z); } private void convertToDegreeMeasure() { center.co_lat = center.co_lat_rad / DEG_TO_RAD_CONV_FACTOR; center.latitude = NINETY - center.co_lat; center.longitude = center.longit_rad / DEG_TO_RAD_CONV_FACTOR; } private void computeCenterAndRadius() { if (type_of_theory == ADAM) computeCenterAndRadiusViaMatrixInversion(); else if (type_of_theory == EDWARD) computeCenterAndRadiusViaVectorAlgebra(); } private void computeCenterAndRadiusViaMatrixInversion() { double det = ZERO; double sum_squares; double[] n = new double[3]; // the determinant of left-side 3 x 3 matrix - det += point[0].x * (point[1].y * point[2].z - point[2].y * point[1].z); det -= point[0].y * (point[1].x * point[2].z - point[2].x * point[1].z); det += point[0].z * (point[1].x * point[2].y - point[2].x * point[1].y); n[0] = ZERO; // expand by minors of first column - n[0] += point[1].y * point[2].z - point[2].y * point[1].z; n[0] -= point[0].y * point[2].z - point[2].y * point[0].z; n[0] += point[0].y * point[1].z - point[1].y * point[0].z; n[1] = ZERO; // expand by minors of second column - n[1] -= point[1].x * point[2].z - point[2].x * point[1].z; n[1] += point[0].x * point[2].z - point[2].x * point[0].z; n[1] -= point[0].x * point[1].z - point[1].x * point[0].z; n[2] = ZERO; // expand by minors of third column - n[2] += point[1].x * point[2].y - point[2].x * point[1].y; n[2] -= point[0].x * point[2].y - point[2].x * point[0].y; n[2] += point[0].x * point[1].y - point[1].x * point[0].y; sum_squares = n[0]*n[0] + n[1]*n[1] + n[2]*n[2]; sum_squares = ONE / Math.sqrt(sum_squares); // normalization factor cosine_radius = sum_squares * det; // cosine of the floating radius if (cosine_radius < ZERO) { cosine_radius = -cosine_radius; n[0] = -n[0]; n[1] = -n[1]; n[2] = -n[2]; } radian_radius = Math.acos(cosine_radius); // Cartesian Coordinates of center point on unit sphere - center = new TeraPoint(sum_squares*n[0], sum_squares*n[1], sum_squares*n[2]); } private void computeCenterAndRadiusViaVectorAlgebra() { } private void convertToOutputUnitsAndDisplayResults() { convertToOutputUnitsAndDisplayDistance(); DisplayCenter(); } private void convertToOutputUnitsAndDisplayDistance() { if (output_unit_type == STATUTE_MILES) reported_radius = MEAN_TERRESTRIAL_RADIUS_IN_STATUTE_MILES * radian_radius; else if (output_unit_type == KILOMETERS) reported_radius = MEAN_TERRESTRIAL_RADIUS_IN_KILOMETERS * radian_radius; else if (output_unit_type == NAUTICAL_MILES) reported_radius = MEAN_TERRESTRIAL_RADIUS_IN_NAUTICAL_MILES * radian_radius; rounded_reported_radius = (float)(Math.round(10.0d*reported_radius)/10.0d); output_text_radius.setText("" + rounded_reported_radius); } private void DisplayCenter() { rounded_reported_latitude = (float)(Math.round(10000.0d*center.latitude) /10000.0d); rounded_reported_longitude = (float)(Math.round(10000.0d*center.longitude)/10000.0d); if ((rounded_reported_latitude >= ZERO) && (rounded_reported_longitude >= ZERO)) output_text_center.setText("" + rounded_reported_latitude + " N; " + rounded_reported_longitude + " E"); else if ((rounded_reported_latitude >= ZERO) && (rounded_reported_longitude < ZERO)) output_text_center.setText("" + rounded_reported_latitude + " N; " + -rounded_reported_longitude + " W"); else if ((rounded_reported_latitude < ZERO) && (rounded_reported_longitude >= ZERO)) output_text_center.setText("" + -rounded_reported_latitude + " S; " + rounded_reported_longitude + " E"); else if ((rounded_reported_latitude < ZERO) && (rounded_reported_longitude < ZERO)) output_text_center.setText("" + -rounded_reported_latitude + " S; " + -rounded_reported_longitude + " W"); } } // end class Distance class TeraPoint { double co_lat; double latitude; double longitude; double co_lat_rad; double longit_rad; double x, y, z; public TeraPoint(double latit, double longit) { latitude = latit; co_lat = 90.0d - latitude; longitude = longit; } public TeraPoint(double x_val, double y_val, double z_val) { x = x_val; y = y_val; z = z_val; } } // end class TeraPoint