File: tutorial_sampled_data.rb

package info (click to toggle)
libtioga-ruby 1.8-1
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 9,956 kB
  • ctags: 3,257
  • sloc: ansic: 31,801; ruby: 16,346; sh: 172; makefile: 114
file content (195 lines) | stat: -rw-r--r-- 7,880 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

module Tioga
module Tutorial

=begin rdoc

= Making plots showing sampled data as a false-color image

Let's finish "plots.rb" with a  look at the code that shows a sampled data array
with contours.  (By the way, the data comes from an astrophysical
calculation.  The temperatures are in Kelvin and the densities are in grams per
cubic centimeter. The array of data gives the calculated opacity for each
temperature and density.  Typical stars have conditions covered by the data.)

link:images/plot_contours.png

This is a more complex example than we've done before, so the amount of code will be
correspondingly larger.  We'll actually start with a somewhat simpler case by doing the
plot without contours.  Then we'll look at the code for adding contour lines.

link:images/samples.png

Here's the top-level routine for this plot:

    def sampled_data
        t.rescale(0.8)
        title = 'Log Opacity'
        t.subplot('right_margin' => @image_right_margin) { 
            sampled_image(title) }
        t.subplot('left_margin' => 0.95, 
            'top_margin' => 0.05, 
            'bottom_margin' => 0.05) { color_bar(title) }
    end
    
It pulls in the right margin and shows the image, then pulls in the other
margins and shows the color bar.  Let's look at the "sampled_image" routine first.

    def sampled_image(title)
        t.do_box_labels(title, 'Log Density', 'Log Temperature')
        data = get_press_image
        xs = @eos_logRHOs
        ys = @eos_logTs
        t.show_plot([@eos_xmin, @eos_xmax, @eos_ymax, @eos_ymin]) do
            t.fill_color = Wheat
            t.fill_frame
            clip_press_image
            t.show_image(
                'll' => [xs.min, ys.min], 
                'lr' => [xs.max, ys.min], 
                'ul' => [xs.min, ys.max], 
                'color_space' => t.mellow_colormap, 
                'data' => data, 'value_mask' => 255,
                'w' => @eos_data_xlen, 'h' => @eos_data_ylen)
        end
    end
    
After showing the title and labels, it calls the "get_press_image" routine in "plots.rb" to
read the data and save it in various local attributes such as "@eos_logRHOs" and "@eos_logTs".
It also calls the FigureMaker create_image_data routine to convert the samples to a form that
is understood by show_image.

    def get_press_image
        @eos_xmin = -8.5; @eos_xmax = 2.5
        @eos_ymin = 5.7; @eos_ymax = 7.0
        @image_zmin = -3
        @image_zmax = 6
        data = Dvector.read("data/logRHOs_for_EoS.data")
        @eos_logRHOs = data[0]
        @eos_data_xlen = @eos_logRHOs.size
        @eos_xmin = @eos_logRHOs.min; @eos_xmax = @eos_logRHOs.max
        data = Dvector.read("data/logTs_for_EoS.data")
        @eos_logTs = data[0]
        @eos_data_ylen = @eos_logTs.size
        @eos_ymin = @eos_logTs.min; @eos_ymax = @eos_logTs.max
        @opacity_data = Dtable.new(@eos_data_xlen, @eos_data_ylen)
        @opacity_data.read("data/Opacity_EoS.data")
        return t.create_image_data(
            @opacity_data,
            'min_value' => @image_zmin, 
            'max_value' => @image_zmax, 
            'masking' => true)
    end
    
Before calling show_image, we set up a clipping region in our
routine, cllp_press_image.  This takes care of the upper left
corner where we don't have valid data. (Our equation of state
code doesn't need to handle those combinations of temperature
and density for our stellar models.)

    def clip_press_image
        t.move_to_point(t.bounds_left, t.bounds_bottom)
        t.append_point_to_path(t.bounds_left, 4.2)
        t.append_point_to_path(-3, t.bounds_top)
        t.append_point_to_path(t.bounds_right, t.bounds_top)
        t.append_point_to_path(t.bounds_right, t.bounds_bottom)
        t.close_path
        t.clip
    end
    
That takes care of the image of the sampled data.  Now let's add the color bar.

    def color_bar(ylabel, levels = nil)
        xmin = 0; xmax = 1; xmid = 0.5
        t.rescale(0.8)
        t.xaxis_type = AXIS_LINE_ONLY
        t.xaxis_loc = BOTTOM
        t.top_edge_type = AXIS_LINE_ONLY
        t.yaxis_loc = t.ylabel_side = RIGHT
        t.yaxis_type = AXIS_WITH_TICKS_AND_NUMERIC_LABELS
        t.left_edge_type = AXIS_WITH_TICKS_ONLY
        t.ylabel_shift += 0.5
        t.yaxis_major_tick_length *= 0.6
        t.yaxis_minor_tick_length *= 0.5
        t.show_ylabel(ylabel); t.no_ylabel
        t.show_plot('boundaries' => [xmin, xmax, @image_zmax, @image_zmin]) do
            t.axial_shading(
                'start_point' => [xmid, @image_zmin], 'end_point' => [xmid, @image_zmax], 
                'colormap' => t.mellow_colormap )
            if levels != nil
                t.stroke_color = Gray
                t.line_width = 1.5
                levels.each { |level| t.stroke_line(xmin, level, xmax, level) }
            end
        end
    end
    
The colors are painted by the axial_shading routine.  The rest of the code
is setting up the axes to give the appearance I happen to like.

Finally, here's the plot routine that includes contour lines.

    def samples_with_contours
        t.rescale(0.8)
        title = 'Log Opacity'
        levels = Array.new
        (-3..5).each { |i| levels << i + 0.4 }
        t.subplot('right_margin' => @image_right_margin) { sampled_image(title) }
        t.subplot('left_margin' => 0.95, 
            'top_margin' => 0.05, 
            'bottom_margin' => 0.05) { color_bar(title, levels) }
        t.subplot('right_margin' => @image_right_margin) do
            t.xaxis_type = t.yaxis_type = AXIS_WITH_TICKS_ONLY
            t.no_title; t.no_xlabel; t.no_ylabel
            bounds = [@eos_xmin, @eos_xmax, @eos_ymax, @eos_ymin]
            t.show_plot(bounds) do
                clip_press_image
                t.stroke_color = SlateGray
                t.line_width = 1
                dest_xs = Dvector.new; dest_ys = Dvector.new; gaps = Array.new
                dict = { 'dest_xs' => dest_xs, 
                        'dest_ys' => dest_ys, 
                        'gaps' => gaps,
                        'xs' => @eos_logRHOs, 
                        'ys' => @eos_logTs,
                        'data' => @opacity_data }
                levels.each do |level|
                    dict['level'] = level
                    t.make_contour(dict)
                    t.append_points_with_gaps_to_path(dest_xs, dest_ys, gaps, true)
                    t.stroke
                end
            end
        end
    end

The first thing it does is show the image and the colorbar. 
Next, it sets up to do a plot with exactly the same frame and bounds
as the image, and then adds the contour lines.  Each line is created by the make_contour
routine and then painted by appending it to the path and calling stroke.  Since the
contour line for particular level may come in many disconnected pieces, we use the
"append_points_with_gaps_to_path" routine which knows how to deal with such things.

BTW: If you're looking for the routine to add labels to contours automatically, you won't find it in tioga.
I've never been particularly pleased with the job done by standard algorithms.  Instead, 
I decided to go at the problem in a different way and give you the actual contour data in
a form that you can work with to make your own choices.  This also means you can try using
different line styles for different contour levels, and perhaps you might even want to use
a legend to identify the levels if there aren't too many.

Of course, if someone develops
a great algorithm for doing contour labels, they can package it up as a Ruby extension
and let us all share.  I'd love to see that happen, and Ruby is a great platform for it.

---

Now let's see how to create a sequence of PDFs and convert it to a movie -- next stop: Animation.

=end

module SampledData
end # module SampledData

end # module Tutorial
end # module Tioga