How do I create skew t and hodograph plots

How do I plot skew-t and hodographs plots to plot it on JavaFX

Can you give an example or link of what you are going to do? The information is a little sparse.

Assume you are running a sufficiently recent octave, openjdk and openjfx on a modern *nix system. Say we save the following script as fx-plot:

#!/bin/sh
#
# fx-plot - plot figure using javafx
# Copyright (C) 2022 Alex Vong
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

#{
# start octave with no window system, otherwise fork() will fail
set -e
OCTAVE="${OCTAVE:-octave}"
export OCTAVE
MKTEMP="${MKTEMP:-mktemp}"
export MKTEMP
JAVAC="${JAVAC:-javac}"
export JAVAC
exec "$OCTAVE" -q -f --no-window-system "$0" "$@"
#}


%%% constants and functions
jars_root = '/usr/share/java';
figure_name = 'figure.png';
figure_fmt = 'png';

dir_exist = @(dir) ischar(dir) && (exist(dir, 'dir') == 7);
recur_rmdir = @(dir) rmdir(dir, 's');

make_frame = @() javaObject('javax.swing.JFrame');
set_frame_size = @(frame, width, height) ...
                  javaMethod('setSize', frame, width, height);
set_frame_exit_on_close = @(frame) ...
                           javaMethod('setDefaultCloseOperation', ...
                                      frame, ...
                                      java_get('javax.swing.JFrame', ...
                                               'EXIT_ON_CLOSE'));
set_frame_visibility = @(frame, visibility) ...
                        javaMethod('setVisible', frame, visibility);

make_fx_panel = @() javaObject('javafx.embed.swing.JFXPanel');
add_panel_to_frame = @(panel, frame) javaMethod('add', frame, panel);
fx_plot = @(panel, imurl) javaMethod('fxplot', 'FXPlot', panel, imurl);

path_to_uri = @(path) javaMethod('toUri', ...
                                 javaMethod('get', ...
                                            'java.nio.file.Paths', ...
                                            path, ...
                                            javaArray('java.lang.String', 0)));
uri_to_url = @(uri) javaMethod('toString', javaMethod('toURL', uri));
path_to_url = @(path) uri_to_url(path_to_uri(path));


%%% if --tmp-dir option is given, output figure to temporary directory and exit
%%% ==> edit to change the output figure <==
if nargin == 2 && isequal(argv{1}, '--tmp-dir')
  if any(strcmp(available_graphics_toolkits(), 'qt'))
    graphics_toolkit('qt');
  end
  tmp_dir = argv{2};
  assert(dir_exist(tmp_dir));

  winds = [161*pi/180 25; ...
           175*pi/180 42; ...
           204*pi/180 54; ...
           229*pi/180 59; ...
           241*pi/180 59];
  directions = winds(:, 1);
  speeds = winds(:, 2);
  x_coords = speeds .* cos(directions);
  y_coords = speeds .* sin(directions);

  if isequal(graphics_toolkit(), 'qt')
    figure('visible', 'off');
  end
  polar(directions, speeds);
  hold('on');
  quiver(zeros(length(winds), 1), ...
         zeros(length(winds), 1), ...
         x_coords, ...
         y_coords, ...
         0);

  figure_path = [tmp_dir filesep() figure_name];
  fmt_opt = ['-d' figure_fmt];
  print(figure_path, fmt_opt);
  quit(0);
end


%%% create temporary directory
mktemp_template = [tempdir() filesep() 'fx-plot.XXXXXXXXXX'];
mktemp = getenv('MKTEMP');
mktemp_args = {'-d', mktemp_template};
[mktemp_in, mktemp_out, mktemp_pid] = popen2(mktemp, mktemp_args);
if mktemp_pid == -1
  error('fx-plot: fail to start subprocess %s', mktemp);
end

assert(fclose(mktemp_in) == 0);

tmp_dir = fgetl(mktemp_out);
mktemp_err = errno();
while mktemp_err == errno('EAGAIN')
  pause(0.1); % wait for 0.1s, see the documentation of popen2 for details
  tmp_dir = fgetl(mktemp_out);
  mktemp_err = errno();
end

assert(dir_exist(tmp_dir));
assert(feof(mktemp_out));
assert(fclose(mktemp_out) == 0);

[mktemp_err, unused, mktemp_msg] = waitpid(mktemp_pid);
if mktemp_err < 0
  error('fx-plot: fail to wait for mktemp %d to terminate: %s', ...
        mktemp_pid, mktemp_msg);
end


%%% ensure parent process cleanup temporary directory
[cleanup_pid, cleanup_fork_msg] = fork();
if cleanup_pid < 0
  error('fx-plot: fail to fork: %s', cleanup_fork_msg);
elseif cleanup_pid > 0 % parent process
  [cleanup_err, unused, cleanup_waitpid_msg] = waitpid(cleanup_pid);
  if cleanup_err < 0
    error('fx-plot: fail to wait for child process %d to terminate: %s', ...
          cleanup_pid, cleanup_waitpid_msg);
  end
  assert(recur_rmdir(tmp_dir) == 1);
  quit(0);
end
%%% | child process continue from here |
%%% V                                  V


%%% initialize classpath-related variables
orig_classpath = getenv('CLASSPATH');
if isempty(orig_classpath)
  jars_path = [jars_root filesep() '*.jar'];
  jars = glob(jars_path);
  classpath = strjoin(jars, ':');
else
  warning('fx-plot: CLASSPATH is set, disable searching jars in %s', ...
          jars_root);
  warning('fx-plot: please ensure CLASSPATH contain javafx jars');
  classpath = orig_classpath;
  jars = strsplit(classpath, ':');
end


%%% outputs src to file in temporary directory
%%% ==> edit to change how javafx plot the figure  <==
src = {'import javafx.application.*;'
       'import javafx.embed.swing.*;'
       'import javafx.scene.*;'
       'import javafx.scene.image.*;'
       'public class FXPlot'
       '{'
       '    public static void fxplot(JFXPanel panel, String imurl)'
       '    {'
       '        Platform.runLater(() ->'
       '        {'
       '            Image im = new Image(imurl);'
       '            ImageView imview = new ImageView();'
       '            imview.setImage(im);'
       '            Group root = new Group();'
       '            root.getChildren().add(imview);'
       '            Scene scene = new Scene(root);'
       '            panel.setScene(scene);'
       '        });'
       '    }'
       '}'};

src_path = [tmp_dir filesep() 'FXPlot.java'];
[src_fd, src_msg] = fopen(src_path, 'w');
if src_fd == -1
  error('fx-plot: fail to create file %s: %s', src_path, src_msg);
end

fprintf(src_fd, strjoin(src));
assert(fclose(src_fd) == 0);


%%% compile src file
[javac_pid, javac_fork_msg] = fork();
if javac_pid < 0
  error('fx-plot: fail to fork: %s', javac_fork_msg);
elseif javac_pid == 0 % child process
  javac = getenv('JAVAC');
  javac_args = {'-classpath', classpath, src_path};
  [unused, javac_exec_msg] = exec(javac, javac_args);
  javac_args
  error('fx-plot: fail to exec %s: %s', javac, javac_exec_msg);
end
%%% | parent process continue from here |
%%% V                                   V


%%% fork-exec another octave process to output figure since qt is not available
%%% when running w/o window system and qt is needed to output figure invisibly
[octave_pid, octave_fork_msg] = fork();
if octave_pid < 0
  error('fx-plot: fail to fork: %s', octave_fork_msg);
elseif octave_pid == 0 % child process
  octave = getenv('OCTAVE');
  octave_args = {'-q', '-f', program_invocation_name, '--tmp-dir', tmp_dir};
  [unused, octave_exec_msg] = exec(octave, octave_args);
  octave_args
  error('fx-plot: fail to exec %s: %s', octave, octave_exec_msg);
end
%%% | parent process continue from here |
%%% V                                   V


%%% wait for all child processes to terminate
while waitpid(-1) > 0
end


%%% plot figure using javafx
javaaddpath(jars);
javaaddpath(tmp_dir);

figure_path = [tmp_dir filesep() figure_name];
figure_info = imfinfo(figure_path);
width = figure_info.Width;
height = figure_info.Height;

frame = make_frame();
set_frame_size(frame, width, height);
set_frame_exit_on_close(frame);
panel = make_fx_panel();
add_panel_to_frame(panel, frame);

set_frame_visibility(frame, true); % visibility need to be set before plotting
fx_plot(panel, path_to_url(figure_path));
pause();

This script uses octave to create a very basic hodograph, save it as png, use javac to compile a javafx program and call the javafx program from octave. You can specify which octave, mktemp and javac to use by setting the environment variables OCTAVE, MKTEMP and JAVAC respectively if necessary.

You also can change the output figure by changing the part with:
==> edit to change the output figure <==

and change how javafx plot the figure by changing the part with:
==> edit to change how javafx plot the figure <==

It would be great to be able to call javafx methods dynamically without compilation. Unfortunately, javafx requires many javafx methods to run inside a dedicated javafx thread. As a result, I don’t think one can call javafx methods using javaMethod dynamically. On the other hand, one might be able to do so with swing.

1 Like