~jstys-z/helioviewer.org/client5

« back to all changes in this revision

Viewing changes to api/modules/lib/HelioviewerTile.php

Modified back-end directory structure and naming conventions. Now using PEAR's naming convention for classes. Tests should be created in mirror sub-directory within the "tests" folder.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?php
2
 
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
 
/**
4
 
 * Helioviewer Tile Class Definition
5
 
 * 
6
 
 * PHP version 5
7
 
 * 
8
 
 * @category WebClient
9
 
 * @package  Helioviewer
10
 
 * @author   Keith Hughitt <keith.hughitt@nasa.gov>
11
 
 * @license  http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
12
 
 * @link     http://launchpad.net/helioviewer.org
13
 
 * 
14
 
 * TODO (2009/12/07)
15
 
 *  To improve smoothness of transparency edges, use a larger mask (e.g. 
16
 
 *  2080x2080  instead of 1040x1040) so that most of scaling will be downwards
17
 
 */
18
 
require_once 'Tile.php';
19
 
 
20
 
class HelioviewerTile extends Tile
21
 
{
22
 
    private $_observatory;
23
 
    private $_instrument;
24
 
    private $_detector;
25
 
    private $_measurement;
26
 
    private $_cacheDir = HV_CACHE_DIR;
27
 
    private $_noImage  = HV_EMPTY_TILE;
28
 
        
29
 
    /**
30
 
     * Helioviewer Tile Constructor
31
 
     * 
32
 
     * @param string $uri       URI for the original JPEG 2000 image
33
 
     * @param int    $x         Helioviewer.org tile x-coordinate
34
 
     * @param int    $y         Helioviewer.org tile y-coordinate
35
 
     * @param int    $zoom      Helioviewer.org zoom-level
36
 
     * @param int    $tileSize  Requested tile size (width and height)
37
 
     * @param int    $jp2Width  Width of the original JP2 image
38
 
     * @param int    $jp2Height Height of the original JP2 image
39
 
     * @param float  $jp2Scale  Plate scale (arc-seconds/pixal) of original image
40
 
     * @param float  $offsetX   Amount original image is offset from sun center (x) 
41
 
     * @param float  $offsetY   Amount original image is offset from sun center (y)
42
 
     * @param string $format    Desired format for resulting tile image
43
 
     * @param string $obs       Observatory
44
 
     * @param string $inst      Instrument
45
 
     * @param string $det       Detector
46
 
     * @param string $meas      Measurement
47
 
     * @param bool   $display   Display the tile immediately or generate only
48
 
     * 
49
 
     * @return void
50
 
     */
51
 
    public function __construct(
52
 
        $uri, $x, $y, $zoom, $tileSize, $jp2Width, $jp2Height, $jp2Scale,  
53
 
        $offsetX, $offsetY, $format, $obs, $inst, $det, $meas, $display = true
54
 
    ) {
55
 
        $this->_observatory = $obs;
56
 
        $this->_instrument  = $inst;
57
 
        $this->_detector    = $det;
58
 
        $this->_measurement = $meas;
59
 
        $this->zoomLevel    = $zoom;
60
 
        $this->offsetX      = $offsetX;
61
 
        $this->offsetY      = $offsetY;
62
 
        
63
 
        $jp2  = HV_JP2_DIR . $uri;
64
 
        $tile = $this->_getTileFilepath($jp2, $x, $y, $format);
65
 
 
66
 
        // If tile already exists in cache, use it
67
 
        // TODO: Once a smarter caching system is in place, take advantage of
68
 
        //       which data we know will be cached (e.g. most recent 2 weeks), and
69
 
        //       skip file_exists check.
70
 
        if (!HV_DISABLE_CACHE && $display) {
71
 
            if (file_exists($tile)) {
72
 
                $this->displayCachedTile($tile);
73
 
                exit();
74
 
            }
75
 
        }
76
 
        $desiredScale = $this->_getImageScale($zoom);
77
 
        
78
 
        // Now we are ready to call the base Tile constructor
79
 
        parent::__construct(
80
 
            $jp2, $tile, $x, $y, $desiredScale, $tileSize, $jp2Width, $jp2Height, $jp2Scale, $format
81
 
        );
82
 
        
83
 
        $colorTable = $this->_getColorTable();
84
 
        
85
 
        if ($colorTable) {
86
 
            $this->setColorTable($colorTable);
87
 
        }
88
 
        
89
 
        if ($this->_instrument == "LASCO") {
90
 
            $this->setAlphaMask(true);
91
 
        }
92
 
            
93
 
        $this->buildImage();
94
 
        
95
 
        if ($display) {
96
 
            $this->display();
97
 
        }
98
 
    }
99
 
        
100
 
    /**
101
 
     * getTileFilePath
102
 
     * 
103
 
     * @param string $jp2    The location of the tile's source JP2 image.
104
 
     * @param int    $x      Tile x-coordinate
105
 
     * @param int    $y      Tile y-coordinate
106
 
     * @param string $format The file format used by the tile
107
 
     *   
108
 
     * @return string The path in the cache where the tile should be stored
109
 
     */
110
 
    private function _getTileFilepath($jp2, $x, $y, $format)
111
 
    {
112
 
        // Base directory
113
 
        $filepath = $this->_cacheDir . "/";
114
 
                
115
 
        // Base filename
116
 
        $exploded = explode("/", $jp2);
117
 
        $filename = substr(end($exploded), 0, -4);
118
 
        
119
 
        // Date information
120
 
        $year  = substr($filename, 0, 4);
121
 
        $month = substr($filename, 5, 2);
122
 
        $day   = substr($filename, 8, 2);
123
 
 
124
 
        $fieldArray = array(
125
 
            $year, $month, $day, $this->_observatory, $this->_instrument, 
126
 
            $this->_detector, $this->_measurement
127
 
        );
128
 
        
129
 
        foreach ($fieldArray as $field) {
130
 
            $filepath .= str_replace(" ", "_", $field) . "/";
131
 
        }
132
 
 
133
 
        // Convert coordinates to strings
134
 
        $xStr = "+" . str_pad($x, 2, '0', STR_PAD_LEFT);
135
 
        if (substr($x, 0, 1) == "-") {
136
 
            $xStr = "-" . str_pad(substr($x, 1), 2, '0', STR_PAD_LEFT);
137
 
        }
138
 
 
139
 
        $yStr = "+" . str_pad($y, 2, '0', STR_PAD_LEFT);
140
 
        if (substr($y, 0, 1) == "-") {
141
 
            $yStr = "-" . str_pad(substr($y, 1), 2, '0', STR_PAD_LEFT);
142
 
        }
143
 
 
144
 
        $filepath .= $filename . "_" . $this->zoomLevel . "_" . $xStr . "_" . $yStr . ".$format";
145
 
 
146
 
        return $filepath;
147
 
    }
148
 
    
149
 
    /**
150
 
     * Translates a given zoom-level into an image plate scale.
151
 
     * 
152
 
     * @param int $zoomLevel Zoom-level for given tile request
153
 
     * 
154
 
     * @return float Returns the arc-seconds/pixal equivalent of the requested zoom-level.
155
 
     */
156
 
    private function _getImageScale($zoomLevel)
157
 
    {
158
 
        $zoomOffset = $zoomLevel - HV_BASE_ZOOM_LEVEL;
159
 
        return HV_BASE_IMAGE_SCALE * (pow(2, $zoomOffset));
160
 
    }
161
 
        
162
 
    /**
163
 
     * Gets the filepath for the color look-up table that corresponds to the image.
164
 
     * 
165
 
     * Note 2009/09/15: Would it make sense to return color table when initially 
166
 
     * looking up image, and pass to tile requests?
167
 
     * 
168
 
     * @return string|bool Returns the filepath for the color lookup table, or false
169
 
     *                     if none is found.
170
 
     */
171
 
    private function _getColorTable()
172
 
    {
173
 
        //$rootdir = substr(getcwd(), 0, -4);
174
 
        
175
 
        if ($this->_detector == "EIT") {
176
 
            return HV_ROOT_DIR . "/images/color-tables/ctable_EIT_" . $this->_measurement . ".png";
177
 
        } else if ($this->_detector == "C2") {
178
 
            return HV_ROOT_DIR .  "/images/color-tables/ctable_idl_3.png";
179
 
        } else if ($this->_detector == "C3") {
180
 
            return HV_ROOT_DIR . "/images/color-tables/ctable_idl_1.png";
181
 
        } else {
182
 
            return false;
183
 
        }       
184
 
    }
185
 
    
186
 
    /**
187
 
     * Generates a portion of an ImageMagick convert command to apply an alpha mask
188
 
     * Note: Values for radii used to generate the LASCO C2 & C3 alpha masks:
189
 
     *  rocc_outer = 7.7;   // (.9625 * orig)
190
 
     *  rocc_inner = 2.415; // (1.05 * orig)
191
 
     *  
192
 
     *  Generating the alpha masks:
193
 
     *      $rsun       = 80.814221; // solar radius in image pixels
194
 
     *      $rocc_inner = 2.415;
195
 
     *      $rocc_outer = 7.7;
196
 
     *      
197
 
     *      // convert to pixels
198
 
     *      $radius_inner = $rocc_inner * $rsun;
199
 
     *      $radius_outer = $rocc_outer * $rsun;
200
 
     *      $innerCircleY = $crpix2 + $radius_inner;
201
 
     *      $outerCircleY = $crpix2 + $radius_outer;
202
 
     *      
203
 
     *      exec("convert -size 1024x1024 xc:black -fill white -draw \"circle $crpix1,$crpix2 $crpix1,$outerCircleY\" 
204
 
     *          -fill black -draw \"circle $crpix1,$crpix2 $crpix1,$innerCircleY\" +antialias LASCO_C2_Mask.png")
205
 
     *      
206
 
     * @param string $input image filepath
207
 
     * 
208
 
     * @return string An imagemagick sub-command for generating the necessary alpha mask
209
 
     */
210
 
    public function applyAlphaMask($input)
211
 
    {
212
 
        $maskWidth  = 1040;
213
 
        $maskHeight = 1040;
214
 
        
215
 
        if ($this->_detector == "C2") {
216
 
            $mask = HV_ROOT_DIR . "/images/alpha-masks/LASCO_C2_Mask.png";
217
 
        } else if ($this->_detector == "C3") {
218
 
            $mask = HV_ROOT_DIR . "/images/alpha-masks/LASCO_C3_Mask.png";
219
 
        }
220
 
 
221
 
        // Ratio of the original image scale to the desired scale
222
 
        $actualToDesired = 1 / $this->desiredToActual;
223
 
 
224
 
        // Determine offset
225
 
        $offsetX = $this->offsetX + (($maskWidth  - $this->jp2Width  + $this->roi["left"])  * $actualToDesired);
226
 
        $offsetY = $this->offsetY + (($maskHeight - $this->jp2Height + $this->roi["top"]) * $actualToDesired);
227
 
        
228
 
        /**
229
 
            $cmd = sprintf(" %s -scale %s %s -alpha Off -compose copy_opacity -composite ", $input, $scale, $mask);
230
 
            $str = " -geometry %s%s %s \( -resize '%s%%' %s \) -alpha Off -compose copy_opacity -composite ";
231
 
            $cmd = sprintf($str, $offsetX, $offsetY, $input, 100 * $actualToDesired, $mask);
232
 
            $str = " %s -extent 512x512 \( -resize '%f%%' -crop %fx%f%+f%+f %s \) -compose copy_opacity " .
233
 
                   "-composite -channel A -threshold 50%% ";
234
 
        */ 
235
 
        $str = " -respect-parenthesis \( %s -gravity SouthWest -background black -extent 512x512 \) " .
236
 
               "\( %s -resize '%f%%' -crop %fx%f%+f%+f +repage -monochrome -gravity SouthWest " .
237
 
               "-background black -extent 512x512 \) -alpha off -compose copy_opacity -composite ";
238
 
        $cmd = sprintf(
239
 
            $str, $input, $mask, 100 * $actualToDesired, 
240
 
            $this->subfieldRelWidth, $this->subfieldRelHeight, $offsetX, $offsetY
241
 
        );
242
 
        
243
 
        return $cmd;
244
 
    }
245
 
    
246
 
    /**
247
 
     * Displays the image on the page
248
 
     *
249
 
     * @param string $tile Filepath to the cached tile.
250
 
     * 
251
 
     * @TODO: Would it be better to make SubFieldImage->display static and call? Or instantiate
252
 
     * super classes (Tile and SubFieldImage), and then call display normally?
253
 
     * 
254
 
     * @return void
255
 
     */
256
 
    public function displayCachedTile($tile)
257
 
    {
258
 
        try {
259
 
            $format = substr($tile, -3);
260
 
            
261
 
            // Cache-Lifetime (in minutes)
262
 
            $lifetime = 60;
263
 
            $exp_gmt = gmdate("D, d M Y H:i:s", time() + $lifetime * 60) ." GMT";
264
 
            header("Expires: " . $exp_gmt);
265
 
            header("Cache-Control: public, max-age=" . $lifetime * 60);
266
 
    
267
 
            // Filename & Content-length
268
 
            $exploded = explode("/", $tile);
269
 
            $filename = end($exploded);
270
 
            
271
 
            $stat = stat($tile);
272
 
            header("Content-Length: " . $stat['size']);
273
 
            header("Content-Disposition: inline; filename=\"$filename\"");    
274
 
 
275
 
            if ($format == "png") {
276
 
                header("Content-Type: image/png");
277
 
            } else {
278
 
                header("Content-Type: image/jpeg");
279
 
            }
280
 
                
281
 
            if (!readfile($tile)) {
282
 
                throw new Exception("Error displaying $filename\n");
283
 
            }
284
 
        } catch (Exception $e) {
285
 
            header("Content-Type: text/html");
286
 
            $msg = "[PHP][" . date("Y/m/d H:i:s") . "]\n\t " . $e->getMessage() . "\n\n";
287
 
            file_put_contents(HV_ERROR_LOG, $msg, FILE_APPEND);
288
 
        }
289
 
    }
290
 
 
291
 
    /**
292
 
     * hasAlphaMask
293
 
     * @return string
294
 
     */
295
 
    //private function hasAlphaMask() {
296
 
    //    return $this->_measurement === "0WL" ? true : false;
297
 
    //}
298
 
}
299
 
?>
 
 
b'\\ No newline at end of file'