ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/JSOC/proj/util/apps/export_as_maproj.c
Revision: 1.4
Committed: Thu Jan 7 00:29:44 2016 UTC (7 years, 8 months ago) by arta
Content type: text/plain
Branch: MAIN
CVS Tags: Ver_8-11, Ver_8-12, Ver_9-2, Ver_9-1, Ver_9-0
Changes since 1.3: +569 -16 lines
Log Message:
Fix for attempts to modify linked keywords, clean-up unused variables, inline all the code dependencies so we dont have to deal with re-definition compiler errors and hard-coding of paths and such.

File Contents

# Content
1 /*
2 * maproj.c ~rick/src/maproj
3 *
4 * Responsible: Rick Bogart RBogart@spd.aas.org
5 *
6 * Map a set of input solar images into a set of output maps
7 *
8 * Parameters: (type default description)
9 * in DataSet TBD Input dataset
10 * A set of images of all or part of the
11 * solar disc in a "plate" coordinate system
12 * (helioprojective geometry).
13 * out DataSer TBD Output data series name
14 * clat double 0.0 Map central heliographic latitude
15 * clon double 0.0 Map central heliographic longitude
16 * scale double 0.0 Scale of map (heliographic degrees /
17 * pixel) at location appropriate for mapping
18 * option; a 0 value implies autoscaling to best
19 * cols int 0 Columns in output maps; 0 -> rows
20 * rows int 0 Rows in output maps; 0 -> cols
21 * map enum "Postel" Mapping option:
22 * recognized values are "carree", "Cassini",
23 * "Mercator", "cyleqa", "sineqa", "gnomonic",
24 * "Postel", "stereographic", "orthographic",
25 * and "Lambert" (and possibly others).
26 * interp enum "cubiconv" Interpolation option:
27 * recognized values are "cubiconv", "nearest",
28 * and "bilinear" (and possibly others)
29 * grid float Unspec If supplied, the spacing in degrees of
30 * a latitude/longitude grid to be overlain on the
31 * output map(s). The overlay value is -Inf where
32 * there would be valid data, +Inf where there is
33 * not. Points are considered on a grid line if
34 * they are within 0.01 * the grid spacing from it
35 * map_pa float 0.0 The angle between heliographic north
36 * and "up" on the output map (in the direction
37 * of increasing rows) [deg[, in the sense that a
38 * positive position angle represents a clockwise
39 * displacement of the north axis.
40 * bscale float 0.0 Value scaling parameter for output
41 * bzero float NaN Value offset parameter for output
42 * clon_key string CRLN_OBS Keyname of float type keyword describing
43 * centre Carrington longitude of each input image
44 * clat_key string CRLT_OBS Keyname of float type keyword describing
45 * centre Carrington latitude of each input image
46 * rsun_key string R_SUN Keyname of float type keyword describing
47 * apparent solar semidiameter of image [pixel]
48 * apsd_key string RSUN_OBS Keyname of float type keyword describing
49 * apparent solar semidiameter of image [arcsec]
50 * dsun_key string DSUN_OBS Keyname of double type keyword describing
51 * r distance from sun for of each image
52 *
53 * Flags
54 * -c center map9s) at image center(s)
55 * -s interpret clon as Stonyhurst rather than Carrington longitude
56 * -v run verbose
57 * -M correct for MDI distortion
58 *
59 * Bugs:
60 * Basic functionality is present, but with several fixed and inappropriate
61 * defaults and some missing arguments
62 * No provision for propagation of default or selected keywords
63 * Uses considerable replicated code from mtrack, esp. array_imaginterp()
64 * function and its dependencies; should be consolidated
65 * Values for Input and Source are inappropriate, refer to whole input
66 * data set
67 * No provision for anisotropic scaling (CDELT1 != CDELT2); PCi_j not
68 * adjusted either
69 * There is evidently no WCS conventional name for the Cassini-Soldner
70 * (transverse plate carree) projection; CAS is arbitrarily used; the
71 * alternative would be to interchange HGLT and HGLN, but that would
72 * necessitate a change in the position angle
73 *
74 * Future Updates
75 * Transformation from one mapping to another
76 *
77 * Revision history is at end of file
78 */
79
80 #include <jsoc_main.h>
81 #include "fstats.h" // XXXX PHS
82
83 #define RECTANGULAR (0)
84 #define CASSINI (1)
85 #define MERCATOR (2)
86 #define CYLEQA (3)
87 #define SINEQA (4)
88 #define GNOMONIC (5)
89 #define POSTEL (6)
90 #define STEREOGRAPHIC (7)
91 #define ORTHOGRAPHIC (8)
92 #define LAMBERT (9)
93
94 #define RSUNM (6.96e8)
95 #define INTERP_NEAREST_NEIGHBOR (1)
96 #define INTERP_BILINEAR (2)
97 /* module identifier */
98 char *module_name = "maproj";
99 char *module_desc = "mapping from solar images";
100 char *version_id = "0.9";
101
102 ModuleArgs_t module_args[] = {
103 {ARG_DATASET, "in", "", "input data set"},
104 {ARG_DATASERIES, "out", "", "output data series"},
105 {ARG_DOUBLE, "clat", "0.0", "heliographic latitude of map center [deg]"},
106 {ARG_DOUBLE, "clon", "0.0", "Carrington longitude of map center [deg]"},
107 {ARG_DOUBLE, "scale", "Not specified", "map scale at center [deg/pxl]"},
108 {ARG_NUME, "map", "orthographic", "map projection",
109 "carree, Cassini, Mercator, cyleqa, sineqa, gnomonic, Postel, stereographic, orthographic, Lambert"},
110 {ARG_NUME, "interp", "cubiconv", "interpolation option",
111 "cubiconv, nearest, bilinear"},
112 {ARG_FLOAT, "grid", "Not Specified",
113 "if specified, spacing of grid overlay [deg]"},
114 {ARG_INT, "cols", "0", "columns in output map"},
115 {ARG_INT, "rows", "0", "rows in output map"},
116 {ARG_FLOAT, "map_pa", "0.0", "position angle of north on output map [deg]"},
117 {ARG_FLOAT, "bscale", "0.0", "output scaling factor"},
118 {ARG_FLOAT, "bzero", "Default", "output offset"},
119 {ARG_STRING, "clon_key", "CRLN_OBS", "keyname for image central longitude"},
120 {ARG_STRING, "clat_key", "CRLT_OBS", "keyname for image central latitude"},
121 {ARG_STRING, "rsun_key", "R_SUN", "keyname for image semi-diameter (pixel)"},
122 {ARG_STRING, "apsd_key", "RSUN_OBS", "keyname for apparent solar semi-diameter (arcsec)"},
123 {ARG_STRING, "dsun_key", "DSUN_OBS", "keyname for observer distance"},
124 {ARG_STRING, "requestid", "none", "RequestID for jsoc export management"}, // XXX PHS
125 {ARG_FLAG, "A", NULL, "Generate output for all input segments"},
126 {ARG_FLAG, "c", "", "center map at center of image"},
127 {ARG_FLAG, "s", "", "clon is Stonyhurst longitude"},
128 {ARG_FLAG, "v", "", "verbose mode"},
129 {ARG_FLAG, "x", "", "write full headers, for export purposes."}, // XXX PHS
130 {}
131 };
132
133 /* global declaration of missing to be initialized as NaN */
134 float missing_val;
135
136 /* From cartography.c. */
137 static double arc_distance (double lat, double lon, double latc, double lonc)
138 {
139 double cosa = sin (lat) * sin (latc) + cos (lat) * cos (latc) * cos (lon - lonc);
140 return acos (cosa);
141 }
142
143 static int plane2sphere (double x, double y, double latc, double lonc, double *lat, double *lon, int projection)
144 {
145 /*
146 * Perform the inverse mapping from rectangular coordinates x, y on a map
147 * in a particular projection to heliographic (or geographic) coordinates
148 * latitude and longitude (in radians).
149 * The map coordinates are first transformed into arc and azimuth coordinates
150 * relative to the center of the map according to the appropriate inverse
151 * transformation for the projection, and thence to latitude and longitude
152 * from the known heliographic coordinates of the map center (in radians).
153 * The scale of the map coordinates is assumed to be in units of radians at
154 * the map center (or other appropriate location of minimum distortion).
155 *
156 * Arguments:
157 * x } Map coordinates, in units of radians at the scale
158 * y } appropriate to the map center
159 * latc Latitude of the map center (in radians)
160 * lonc Longitude of the map center (in radians)
161 * *lat Returned latitude (in radians)
162 * *lon Returned longitude (in radians)
163 * projection A code specifying the map projection to be used: see below
164 *
165 * The following projections are supported:
166 * RECTANGULAR A "rectangular" mapping of x and y directly to
167 * longitude and latitude, respectively; it is the
168 * normal cylindrical equidistant projection (plate
169 * carrĂˆe) tangent at the equator and equidistant
170 * along meridians. Central latitudes off the equator
171 * merely result in displacement of the map in y
172 * Also known as CYLEQD
173 * CASSINI The transverse cylindrical equidistant projection
174 * (Cassini-Soldner) equidistant along great circles
175 * perpendicular to the central meridian
176 * MERCATOR Mercator's conformal projection, in which paths of
177 * constant bearing are straight lines
178 * CYLEQA Lambert's normal equal cylindrical (equal-area)
179 * projection, in which evenly-spaced meridians are
180 * evenly spaced in x and evenly-spaced parallels are
181 * separated by the cosine of the latitude
182 * SINEQA The Sanson-Flamsteed sinusoidal equal-area projection,
183 * in which evenly-spaced parallels are evenly spaced in
184 * y and meridians are sinusoidal curves
185 * GNOMONIC The gnomonic, or central, projection, in which all
186 * straight lines correspond to geodesics; projection
187 * from the center of the sphere onto a tangent plane
188 * POSTEL Postel's azimuthal equidistant projection, in which
189 * straight lines through the center of the map are
190 * geodesics with a uniform scale
191 * STEREOGRAPHIC The stereographic projection, mapping from the
192 * antipode of the map center onto a tangent plane
193 * ORTHOGRAPHIC The orthographic projection, mapping from infinity
194 * onto a tangent plane
195 * LAMBERT Lambert's azimuthal equivalent projection
196 *
197 * The function returns -1 if the requested point on the map does not project
198 * back to the sphere or is not a principal value, 1 if it projects to a
199 * point on a hidden hemisphere (if that makes sense), 0 otherwise
200 */
201 static double latc0 = 0.0, sinlatc = 0.0, coslatc = 1.0 ;
202 double r, rm, test;
203 double cosr, sinr, cosp, sinp, coslat, sinlat, sinlon;
204 double sinphi, cosphi, phicom;
205 int status = 0;
206
207 if (latc != latc0) {
208 coslatc = cos (latc);
209 sinlatc = sin (latc);
210 }
211 latc0 = latc;
212
213 switch (projection) {
214 case (RECTANGULAR):
215 *lon = lonc + x;
216 *lat = latc + y;
217 if (arc_distance (*lat, *lon, latc, lonc) > M_PI_2) status = 1;
218 if (fabs (x) > M_PI || fabs (y) > M_PI_2) status = -1;
219 return status;
220 case (CASSINI): {
221 double sinx = sin (x);
222 double cosy = cos (y + latc);
223 double siny = sin (y + latc);
224 *lat = acos (sqrt (cosy * cosy + siny * siny * sinx * sinx));
225 if (y < -latc) *lat *= -1;
226 *lon = (fabs (*lat) < M_PI_2) ? lonc + asin (sinx / cos (*lat)) : lonc;
227 if (y > (M_PI_2 - latc) || y < (-M_PI_2 - latc))
228 *lon = 2 * lonc + M_PI - *lon;
229 if (*lon < -M_PI) *lon += 2* M_PI;
230 if (*lon > M_PI) *lon -= 2 * M_PI;
231 if (arc_distance (*lat, *lon, latc, lonc) > M_PI_2) status = 1;
232 if (fabs (x) > M_PI || fabs (y) > M_PI_2) status = -1;
233 return status;
234 }
235 case (CYLEQA):
236 if (fabs (y) > 1.0) {
237 y = copysign (1.0, y);
238 status = -1;
239 }
240 cosphi = sqrt (1.0 - y*y);
241 *lat = asin ((y * coslatc) + (cosphi * cos (x) * sinlatc));
242 test = (cos (*lat) == 0.0) ? 0.0 : cosphi * sin (x) / cos (*lat);
243 *lon = asin (test) + lonc;
244 if (fabs (x) > M_PI_2) {
245 status = 1;
246 while (x > M_PI_2) {
247 *lon = M_PI - *lon;
248 x -= M_PI;
249 }
250 while (x < -M_PI_2) {
251 *lon = -M_PI - *lon;
252 x += M_PI;
253 }
254 }
255 if (arc_distance (*lat, *lon, latc, lonc) > M_PI_2) status = 1;
256 return status;
257 case (SINEQA):
258 cosphi = cos (y);
259 if (cosphi <= 0.0) {
260 *lat = y;
261 *lon = lonc;
262 if (cosphi < 0.0) status = -1;
263 return status;
264 }
265 *lat = asin ((sin (y) * coslatc) + (cosphi * cos (x/cosphi) * sinlatc));
266 coslat = cos (*lat);
267 if (coslat <= 0.0) {
268 *lon = lonc;
269 if (coslat < 0.0) status = 1;
270 return status;
271 }
272 test = cosphi * sin (x/cosphi) / coslat;
273 *lon = asin (test) + lonc;
274 if (fabs (x) > M_PI * cosphi) return (-1);
275 /*
276 if (fabs (x) > M_PI_2) {
277 status = 1;
278 while (x > M_PI_2) {
279 *lon = M_PI - *lon;
280 x -= M_PI;
281 }
282 while (x < -M_PI_2) {
283 *lon = -M_PI - *lon;
284 x += M_PI;
285 }
286 */
287 if (fabs (x) > M_PI_2 * cosphi) {
288 status = 1;
289 while (x > M_PI_2 * cosphi) {
290 *lon = M_PI - *lon;
291 x -= M_PI * cosphi;
292 }
293 while (x < -M_PI_2 * cosphi) {
294 *lon = -M_PI - *lon;
295 x += M_PI * cosphi;
296 }
297 }
298 /*
299 if (arc_distance (*lat, *lon, latc, lonc) > M_PI_2) status = 1;
300 */
301 return status;
302 case (MERCATOR):
303 phicom = 2.0 * atan (exp (y));
304 sinphi = -cos (phicom);
305 cosphi = sin (phicom);
306 *lat = asin ((sinphi * coslatc) + (cosphi * cos (x) * sinlatc));
307 *lon = asin (cosphi * sin (x) / cos (*lat)) + lonc;
308 if (arc_distance (*lat, *lon, latc, lonc) > M_PI_2) status = 1;
309 if (fabs (x) > M_PI_2) status = -1;
310 return status;
311 }
312 /* Convert to polar coordinates */
313 r = hypot (x, y);
314 cosp = (r == 0.0) ? 1.0 : x / r;
315 sinp = (r == 0.0) ? 0.0 : y / r;
316 /* Convert to arc */
317 switch (projection) {
318 case (POSTEL):
319 rm = r;
320 if (rm > M_PI_2) status = 1;
321 break;
322 case (GNOMONIC):
323 rm = atan (r);
324 break;
325 case (STEREOGRAPHIC):
326 rm = 2 * atan (0.5 * r);
327 if (rm > M_PI_2) status = 1;
328 break;
329 case (ORTHOGRAPHIC):
330 if ( r > 1.0 ) {
331 r = 1.0;
332 status = -1;
333 }
334 rm = asin (r);
335 break;
336 case (LAMBERT):
337 if ( r > 2.0 ) {
338 r = 2.0;
339 status = -1;
340 }
341 rm = 2 * asin (0.5 * r);
342 if (rm > M_PI_2 && status == 0) status = 1;
343 break;
344 }
345 cosr = cos (rm);
346 sinr = sin (rm);
347 /* Convert to latitude-longitude */
348 sinlat = sinlatc * cosr + coslatc * sinr * sinp;
349 *lat = asin (sinlat);
350 coslat = cos (*lat);
351 sinlon = (coslat == 0.0) ? 0.0 : sinr * cosp / coslat;
352 /* This should never happen except for roundoff errors, but just in case: */
353 /*
354 if (sinlon + 1.0 <= 0.0) *lon = -M_PI_2;
355 else if (sinlon - 1.0 >= 0.0) *lon = M_PI_2;
356 else
357 */
358 *lon = asin (sinlon);
359 /* Correction suggested by Dick Shine */
360 if (cosr < (sinlat * sinlatc)) *lon = M_PI - *lon;
361 *lon += lonc;
362 return status;
363 }
364
365 /* From imginfo.c. */
366 #define NO_SEMIDIAMETER (0x0002)
367 #define NO_XSCALE (0x0004)
368 #define NO_YSCALE (0x0008)
369 #define NO_XCENTERLOC (0x0010)
370 #define NO_YCENTERLOC (0x0020)
371 #define NO_HELIO_LATC (0x0040)
372 #define NO_HELIO_LONC (0x0080)
373 #define NO_HELIO_PA (0x0100)
374 #define NO_XUNITS (0x0200)
375 #define NO_YUNITS (0x0400)
376 #define NO_OBSERVER_LAT (0x0002)
377 #define NO_OBSERVER_LON (0x0004)
378
379 #define KEYSCOPE_VARIABLE (0x80000000)
380
381 typedef struct paramdef {
382 double scale;
383 double offset;
384 double defval;
385 unsigned int statusbit;
386 char name[32];
387 } ParamDef;
388
389
390 static double lookup (DRMS_Record_t *rec, ParamDef key, int *status) {
391 double value = key.defval;
392 int lookupstat = 0;
393
394 value = drms_getkey_double (rec, key.name, &lookupstat);
395 value = value * key.scale + key.offset;
396 if (lookupstat) *status |= key.statusbit;
397 if (isnan (value)) *status |= key.statusbit;
398 return value;
399 }
400
401 static char *lookup_str (DRMS_Record_t *rec, ParamDef key, int *status) {
402 DRMS_Keyword_t *keywd;
403 int lstat;
404 char *value;
405
406 value = drms_getkey_string (rec, key.name, &lstat);
407 if (lstat) *status |= key.statusbit;
408 /* cadence should be constant */
409 if ((keywd = drms_keyword_lookup (rec, key.name, 1))) {
410 if (keywd->info->recscope != 1) *status |= KEYSCOPE_VARIABLE;
411 } else *status |= key.statusbit;
412 return value;
413 }
414
415 static int solar_image_info (DRMS_Record_t *img, double *xscl, double *yscl,
416 double *ctrx, double *ctry, double *apsd, const char *rsun_key,
417 const char *apsd_key, double *pang, double *ellipse_e, double *ellipse_pa,
418 int *x_invrt, int *y_invrt, int *need_ephem, int AIPS_convention) {
419 /*
420 * Provides the following values from the DRMS record:
421 * xscl scale in the image column direction (arc-sec/pixel)
422 * yscl scale in the image row direction (arc-sec/pixel)
423 * ctrx (virtual) fractional pixel column of the center of the
424 * solar disc
425 * ctry (virtual) fractional pixel row of the center of the
426 * solar disc
427 * apsd apparent semi-diameter (semimajor-axis) of the solar disc, in
428 * pixel units
429 * pang position angle of solar north relative to image vertical (y-axis,
430 * [0,0] -> [0,1]), measured westward (clockwise), in radians
431 * eecc eccentricity of best-fit ellipse describing limb
432 * eang position angle of best-fit ellipse describing limb, relative
433 * to direction of solar north, measured westward (clockwise), in radians
434 * xinv 0 if image is direct, 1 if flipped by columns
435 * yinv 0 if image is direct, 1 if flipped by rows
436 * If AIPS_convention is true (!=0), it is assumed that the input keywords
437 * representing position and ellipse angles are measured westward
438 * (clockwise) relative to their nominal axes; otherwise they are measured
439 * eastward (counter-clockwise) relative to their nominal axes.
440 * NO!
441 * The following data types are supported: SOHO-MDI, GONG+, Mt. Wilson MOF,
442 * SOHO-EIT, TRACE, BBSO Ha
443 * NO!
444 * If the data are not recognizably of one of these types, the function
445 * returns NO_DATA_DICT; if one or more required keywords are missing
446 * the function returns a status mask indicating which values could not
447 * be filled. No flag is set if the image ellipse parameters are not
448 * available (unless they are required for other parameters), but the
449 * eccentricity and pericenter angle are quietly set to 0.
450 */
451 enum param {
452 XSCL, YSCL, XUNI, YUNI, LATC, LONC, CTRX, CTRY, PANG, APSD, RSUN,
453 ESMA, ESMI, EECC, EANG, XASP, YASP, PCT
454 };
455 enum known_plat {
456 UNKNOWN
457 };
458 static ParamDef param[PCT];
459 static double raddeg = M_PI / 180.0;
460 static double degrad = 180.0 / M_PI;
461 double ella, ellb;
462 int n, status = 0;
463 static int scale_avail, xinv_type, yinv_type;
464 static int hdrtype = UNKNOWN, lasthdr = UNKNOWN - 1;
465 char *strval;
466 /*
467 * Set up the appropriate dictionary for interpretation of keywords
468 */
469 if (lasthdr != hdrtype) {
470 if (lasthdr >= UNKNOWN)
471 fprintf (stderr,
472 "Warning from solar_image_info(): record keywords may change\n");
473 for (n = 0; n < PCT; n++) {
474 sprintf (param[n].name, "No Keyword");
475 param[n].scale = 1.0;
476 param[n].offset = 0.0;
477 param[n].defval = NAN;
478 param[n].statusbit = 0;
479 }
480 param[RSUN].statusbit = NO_SEMIDIAMETER;
481 param[APSD].statusbit = NO_SEMIDIAMETER;
482 param[XSCL].statusbit = NO_XSCALE;
483 param[YSCL].statusbit = NO_YSCALE;
484 param[XUNI].statusbit = NO_XUNITS;
485 param[YUNI].statusbit = NO_YUNITS;
486 param[CTRX].statusbit = NO_XCENTERLOC;
487 param[CTRY].statusbit = NO_YCENTERLOC;
488 param[CTRX].statusbit = NO_XCENTERLOC;
489 param[CTRY].statusbit = NO_YCENTERLOC;
490 param[LATC].statusbit = NO_HELIO_LATC;
491 param[LONC].statusbit = NO_HELIO_LONC;
492 param[PANG].statusbit = NO_HELIO_PA;
493 param[EECC].defval = 0.0;
494 param[EANG].defval = 0.0;
495 param[XASP].defval = 1.0;
496 param[YASP].defval = 1.0;
497
498 switch (hdrtype) {
499 default: /* Assume WCS HPLN/T */
500 /* WITH CERTAIN MDI SPECIFIC ONES */
501 scale_avail = 1;
502 xinv_type = yinv_type = 0;
503 sprintf (param[XUNI].name, "CUNIT1");
504 sprintf (param[YUNI].name, "CUNIT2");
505 sprintf (param[XSCL].name, "CDELT1");
506 sprintf (param[YSCL].name, "CDELT2");
507 sprintf (param[CTRX].name, "CRPIX1");
508 param[CTRX].offset = -1.0;
509 sprintf (param[CTRY].name, "CRPIX2");
510 param[CTRY].offset = -1.0;
511 *need_ephem = 0;
512 strval = lookup_str (img, param[XUNI], &status);
513 if (!(status & NO_XUNITS)) {
514 if (!strcmp (strval, "arcsec")) param[XSCL].scale = 1.0;
515 else if (!strcmp (strval, "arcmin")) param[XSCL].scale = 1.0 / 60.0;
516 else if (!strcmp (strval, "deg")) param[XSCL].scale = 1.0 / 3600.0;
517 else if (!strcmp (strval, "mas")) param[XSCL].scale = 1000.0;
518 else if (!strcmp (strval, "rad")) param[XSCL].scale = degrad * 3600.0;
519 /*
520 need_units = status & KEYSCOPE_VARIABLE;
521 */
522 }
523 if (strval) free (strval);
524 strval = lookup_str (img, param[YUNI], &status);
525 if (!(status & NO_YUNITS)) {
526 if (!strcmp (strval, "arcsec")) param[YSCL].scale = 1.0;
527 else if (!strcmp (strval, "arcmin")) param[YSCL].scale = 1.0 / 60.0;
528 else if (!strcmp (strval, "deg")) param[YSCL].scale = 1.0 / 3600.0;
529 else if (!strcmp (strval, "mas")) param[YSCL].scale = 1000.0;
530 else if (!strcmp (strval, "rad")) param[YSCL].scale = degrad * 3600.0;
531 /*
532 need_units = status & KEYSCOPE_VARIABLE;
533 */
534 }
535 if (strval) free (strval);
536 /* the following are appropriate for MDI, but not strictly based on WCS */
537 /*
538 sprintf (param[RSUN].name, "R_SUN");
539 sprintf (param[APSD].name, "OBS_ASD");
540 */
541 strncpy (param[RSUN].name, rsun_key, 31);
542 strncpy (param[APSD].name, apsd_key, 31);
543 sprintf (param[PANG].name, "CROTA2");
544 param[PANG].scale = -raddeg;
545 if (AIPS_convention) param[PANG].scale *= -1;
546 sprintf (param[ESMA].name, "S_MAJOR");
547 sprintf (param[ESMI].name, "S_MINOR");
548 sprintf (param[EANG].name, "S_ANGLE");
549 param[EANG].scale = -raddeg;
550 if (AIPS_convention) param[EANG].scale *= -1;
551 }
552 }
553 lasthdr = hdrtype;
554 /* Plate info: image scale, distortion */
555 *apsd = lookup (img, param[RSUN], &status);
556 if (scale_avail) {
557 *xscl = lookup (img, param[XSCL], &status);
558 *yscl = lookup (img, param[YSCL], &status);
559 if (status & NO_SEMIDIAMETER) {
560 status &= ~NO_SEMIDIAMETER;
561 *apsd = lookup (img, param[APSD], &status);
562 if (status & NO_SEMIDIAMETER) {
563 *need_ephem = 1;
564 } else {
565 if (!(status & (NO_XSCALE | NO_YSCALE))) {
566 *apsd /= (*xscl <= *yscl) ? *xscl : *yscl;
567 status &= ~NO_SEMIDIAMETER;
568 }
569 }
570 }
571 }
572 ella = lookup (img, param[ESMA], &status);
573 ellb = lookup (img, param[ESMI], &status);
574 *ellipse_e = sqrt ((ella - ellb) * (ella + ellb)) / ella;
575 *ellipse_pa = lookup (img, param[EANG], &status);
576 /* Pointing (attitude: image location) */
577 *ctrx = lookup (img, param[CTRX], &status);
578 *ctry = lookup (img, param[CTRY], &status);
579 /* Position angle of solar north */
580 *pang = lookup (img, param[PANG], &status);
581 /* Image orientation */
582 *x_invrt = xinv_type;
583 *y_invrt = yinv_type;
584
585 return status;
586 }
587
588 /* From mdistuff.c. */
589 #define MDI_IMG_ACPA (1.01e-3)
590 #define MDI_IMG_ASPA (-1.49e-3)
591
592 static void mtrack_MDI_correct_imgctr (double *xc, double *yc, double rsun) {
593 double rs2;
594
595 rs2 = rsun * rsun / 512.0;
596 *xc -= MDI_IMG_ASPA * rs2;
597 *yc -= MDI_IMG_ACPA * rs2;
598 }
599
600 /*
601 * mtrack_MDI_image_tip
602 *
603 * Correct for ellipticity of image due to plate tipping
604 * The constants are:
605 * TIP = 2.61 deg = 0.04555
606 * EFL = 25.3 (effective focal length in units of plate half-width)
607 * PA = -56 deg
608 * SPA = sin (PA), CPA = cos (PA)
609 * AEP = TIP / EFL
610 * BEP = TIP^2 / 4 = 5.187e-4
611 * BCPA = BEP * CPA, BSPA = BEP * SPA
612 *
613 * Bugs:
614 * There is no check that |direct| = 1; it can actually be changed as a
615 * scale factor (useful for testing)
616 *
617 */
618
619 #define MDI_IMG_SPA (-0.8290)
620 #define MDI_IMG_CPA (0.5592)
621 #define MDI_IMG_AEP (1.80e-3)
622 #define MDI_IMG_BCPA (2.90e-4)
623 #define MDI_IMG_BSPA (-4.30e-4)
624
625 static void mtrack_MDI_image_tip (double *x, double *y, int n, int direct) {
626 double x0, y0, s, t;
627
628 while (n--) {
629 x0 = *x;
630 y0 = *y;
631 t = direct * (MDI_IMG_SPA * x0 + MDI_IMG_CPA * y0);
632 s = direct * (MDI_IMG_CPA * x0 - MDI_IMG_SPA * y0);
633 *x += MDI_IMG_BSPA * t;
634 *y += MDI_IMG_BCPA * t;
635 *x -= MDI_IMG_BCPA * s;
636 *y += MDI_IMG_BSPA * s;
637 t *= MDI_IMG_AEP;
638 *x++ += t * x0;
639 *y++ += t * y0;
640 }
641 }
642
643 /*
644 * mtrack_MDI_image_stretch
645 *
646 * Modify "plate" coordinates to account for known optical distortions
647 * in the MDI instrument
648 * It is assumed that the coordinates *x and *y are in terms of half the
649 * full plate width, i.e. that they are in the range [-1.0, 1.0] and
650 * relative to its center; this of course requires an external correction
651 * to be applied in the cases of extracted rasters and binned data.
652 * For MDI the half-plate-width is 512 * 21 um. The 2nd-order radial
653 * correction constant is given in Kuhn et al. (Ap.J. 613, 1241; 2004)
654 * as 1.58e-3 where the radial unit is in cm. The constant used here is thus
655 * 1.58e-3 * (.0021 * 512)^2
656 *
657 * By ignoring the 4th-order term the function can be used for inverse
658 * as well as direct stretching.
659 *
660 * The value of the integer direct specifies the direction of the
661 * transformation: +1 for a correction from "perfect" to plate coordinates,
662 * -1 for transformation from plate to perfect
663 *
664 * Bugs:
665 * There is no check that |direct| = 1; it can actually be changed as a
666 * scale factor (useful for testing)
667 *
668 */
669
670 #define MDI_IMG_STRETCH (1.83e-3)
671
672 static void mtrack_MDI_image_stretch (double *x, double *y, int n, int direct) {
673 double f, r2, s;
674
675 s = direct * MDI_IMG_STRETCH;
676 while (n--) {
677 r2 = *x * *x + *y * *y;
678 f = 1.0 + s * r2;
679 *x++ *= f;
680 *y++ *= f;
681 }
682 }
683
684 #define MDI_IMG_SOHO_PA (-0.2)
685
686 static void mtrack_MDI_correct_pa (double *pa) {
687 *pa += MDI_IMG_SOHO_PA * M_PI / 180.0;
688 }
689
690
691 /* Calculate the interpolation kernel. */
692 /* Robert G. Keys, "Cubic Convolution Interpolation for digital Image Processing", IEEE
693 * Transactions on Acoustics, Speech, and Signal Processing, Vol ASSP-29, No. 6, December 1981.
694 */
695 void ccker (double *u, double s) {
696 double s2, s3;
697
698 s2= s * s;
699 s3= s2 * s;
700 u[0] = s2 - 0.5 * (s3 + s);
701 u[1] = 1.5*s3 - 2.5*s2 + 1.0;
702 u[2] = -1.5*s3 + 2.0*s2 + 0.5*s;
703 u[3] = 0.5 * (s3 - s2);
704 }
705
706 /* note that the end points do not need to be discarded using:
707 * equiv of for f[0] = 3*data[0] - 3*data[1] + data[2];
708 * and for f[n-1] = 3*data[n-1] - 3*data[n-2] + data[n-3];
709 * for target points from x=0 to x=1, and x=n-2 to x=n-2.
710 * see equation after eqn 25 in Keys' paper
711 */
712
713 /* Cubic convolution interpolation */
714 float ccint2 (float *f, int nx, int ny, double x, double y) {
715 double ux[4], uy[4];
716 double sum;
717 int ix, iy, ix1, iy1, i, j;
718
719 if (x < 1.0 || x >= (float)(nx-2) || y < 1.0 || y >= (float)(ny-2))
720 return missing_val;
721
722 ix = (int)x;
723 ccker (ux, x - (double)ix);
724 iy = (int)y;
725 ccker (uy, y - (double)iy);
726
727 ix1 = ix - 1;
728 iy1 = iy - 1;
729 sum = 0.;
730 for (i = 0; i < 4; i++)
731 for (j = 0; j < 4; j++)
732 sum = sum + f[(iy1+i) * nx + ix1 + j] * uy[i] * ux[j];
733 return (float)sum;
734 }
735 /* Bilinear interpolation */
736 float linint2 (float *f, int cols, int rows, double x, double y) {
737 double p, q, val;
738 int col = (int)x, row = (int)y;
739 int onerow = cols * row;
740 int colp1 = col + 1, onerowp1 = onerow + cols;
741
742 if (x < 0.0 || x > cols || y < 0.0 || y >= rows)
743 return missing_val;
744 p = x - col;
745 q = y - row;
746 val = (1 - p) * (1 - q) * f[col + onerow]
747 + p * (1 - q) * f[colp1 + onerow]
748 + (1 - p) * q * f[col + onerowp1]
749 + p * q * f[colp1 + onerowp1];
750 return val;
751 }
752 /* nearest value "interpolation" */
753 float nearest_val (float *f, int cols, int rows, double x, double y) {
754 int col, row;
755 if (x < -0.5 || x >= (cols - 0.5) || y < -0.5 || y >= (rows - 0.5))
756 return missing_val;
757 col = x + 0.5;
758 row = y + 0.5;
759 return f[col + row * cols];
760 }
761
762 float array_imaginterp (DRMS_Array_t *img, double x, double y,
763 int schema) {
764 /*
765 * Interpolate to an arbitrary grid location {x, y} from a DRMS Array
766 * containing a projected solar image. The aim of this function is
767 * is to provide an ideal interpolation weighted by foreshortening,
768 * limb darkening, and vector projection, but for now this is simply
769 * a stub function that extracts information from the attributes of
770 * the dataset and calls a simple two-dimensional cubic convolutional
771 * interpolation function.
772 *
773 * x and y are in the range [-1,-1] at the "lower left" of the first pixel
774 * to [1,1] at the "upper right" of the last pixel in the image.
775 * (These are converted to the ccint2 conventions, with x and y in
776 * the range [0,0] at the "center" of the first pixel to
777 * [cols-1, rows-1] at the "center" of the last pixel.)
778 * Interpolation near the edges is not allowed.
779 *
780 * Bugs:
781 * Interpolation within one pixel of edge is not implemented. If x or y
782 * is in this range or off the image, the function returns zero.
783 * The function assumes a fixed scale in both directions, so that if one
784 * dimension is larger than another the scale is applied to the larger.
785 * Only floating point data are supported by the function, and there is
786 * not even any testing for validity.
787 */
788 double xs, ys;
789 int cols, rows, mdim;
790
791 cols = img->axis[0];
792 rows = img->axis[1];
793 mdim = (cols > rows) ? cols : rows;
794 xs = 0.5 * (x + 1.0) * mdim - 0.5;
795 ys = 0.5 * (y + 1.0) * mdim - 0.5;
796 if (schema == INTERP_NEAREST_NEIGHBOR)
797 return nearest_val (img->data, cols, rows, xs, ys);
798 else if (schema == INTERP_BILINEAR)
799 return linint2 (img->data, cols, rows, xs, ys);
800 else return ccint2 (img->data, cols, rows, xs, ys);
801 }
802
803 static void perform_mapping (DRMS_Array_t *img, float *map,
804 double *maplat, double *maplon, double *map_coslat, double *map_sinlat,
805 int pixct, unsigned char *offsun, double latc, double lonc,
806 double xc, double yc, double radius, double pa, double ellipse_e,
807 double ellipse_pa, int x_invrt, int y_invrt, int interpolator,
808 int MDI_correct_distort) {
809 /*
810 * Perform the mappings from the target heliographic coordinate sets
811 * appropriate to each output cube into the image coordinates (as
812 * corrected) for spatial interpolation of the data values
813 */
814 static double sin_asd = 0.004660, cos_asd = 0.99998914;
815 /* appropriate to 1 AU */
816 double r, cos_cang, xr, yr;
817 double cos_lat, sin_lat, lon, cos_lat_lon;
818 double xx, yy;
819 float interpval;
820 int n;
821
822 double cos_pa = cos (pa);
823 double sin_pa = sin (pa);
824 double cos_latc = cos (latc);
825 double sin_latc = sin (latc);
826 int plate_cols = img->axis[0];
827 int plate_rows = img->axis[1];
828 double plate_width = (plate_cols > plate_rows) ? plate_cols : plate_rows;
829
830 xc *= 2.0 / plate_width;
831 yc *= 2.0 / plate_width;
832 radius *= 2.0 / plate_width;
833
834 for (n = 0; n < pixct; n++) {
835 /* Calculate heliographic coordinates corresponding to map location */
836 if (offsun[n]) {
837 map[n] = missing_val;
838 continue;
839 }
840 sin_lat = map_sinlat[n];
841 cos_lat = map_coslat[n];
842 lon = maplon[n];
843 cos_lat_lon = cos_lat * cos (lon - lonc);
844 cos_cang = sin_lat * sin_latc + cos_latc * cos_lat_lon;
845 if (cos_cang < 0.0) {
846 map[n] = missing_val;
847 continue;
848 }
849 r = radius * cos_asd / (1.0 - cos_cang * sin_asd);
850 xr = r * cos_lat * sin (lon - lonc);
851 yr = r * (sin_lat * cos_latc - sin_latc * cos_lat_lon);
852 xx = xr * cos_pa - yr * sin_pa;
853 yy = xr * sin_pa + yr * cos_pa;
854 yy += yc;
855 xx += xc;
856 /* should take tests outside loop, just modify xc and yc */
857 if (plate_cols > plate_rows) yy -= 1.0 - plate_rows / plate_width;
858 if (plate_rows > plate_cols) xx -= 1.0 - plate_cols / plate_width;
859 interpval = array_imaginterp (img, xx, yy, interpolator);
860 /* Correction for MDI distortion and tip */
861 /* should be replaced by call to MDI_correct_plateloc when verified */
862 if (MDI_correct_distort) {
863 mtrack_MDI_image_tip (&xx, &yy, 1, 1);
864 mtrack_MDI_image_stretch (&xx, &yy, 1, 1);
865 }
866 map[n] = (isnan (interpval)) ? missing_val : interpval;
867 }
868 }
869
870 int near_grid_line (double lon, double lat, double grid, double near) {
871 /*
872 * Return 1 if a target point (lon, lat) is within (near) deg. of a grid line
873 * with spacing (grid) deg.
874 */
875 static double degrad = 180.0 / M_PI;
876 double g2 = 0.5 * grid;
877
878 lon *= degrad;
879 lat *= degrad;
880
881 while (lon < 0.0) lon += grid;
882 while (lon > g2) lon -= grid;
883 if (fabs (lon) < near) return 1;
884 while (lat < 0.0) lat += grid;
885 while (lat > g2) lat -= grid;
886 if (fabs (lat) < near) return 1;
887 return 0;
888 }
889
890 /* These check_and_set_key_* functions were copied from keystuff.c. I made a number of changes -
891 * the functions used to return success if the key did not exist. They also
892 * used to follow links and attempt to set linked keywords, but linked keywords
893 * are generally in read-only records, so I think this was incorrect.
894 * And they did not check the return value of the lower-level DRMS set-key functions. */
895 static int check_and_set_key_str(DRMS_Record_t *outRec, const char *key, const char *val)
896 {
897 DRMS_Keyword_t *keywd = NULL;
898 char *vreq;
899 int status;
900 int errOut;
901
902 errOut = 0;
903
904 if (!(keywd = drms_keyword_lookup(outRec, key, 0))) return 1;
905
906 if (!drms_keyword_isconstant(keywd))
907 {
908 /* it's not a constant, so don't worry, just set it */
909 return (drms_setkey_string(outRec, key, val) != DRMS_SUCCESS);
910 }
911
912 vreq = drms_getkey_string(outRec, key, &status);
913 if (status || !vreq)
914 {
915 fprintf (stderr, "Error retrieving value for constant keyword %s\n", key);
916 errOut = 1;
917 }
918
919 if (strcmp(vreq, val))
920 {
921 char format[256];
922
923 snprintf (format, sizeof(format), "Error: new value \"%s\" for constant keyword %%s\n differs from the existing value \"%s\"\n", keywd->info->format, keywd->info->format);
924 fprintf(stderr, format, val, key, vreq);
925 errOut = 1;
926 }
927
928 if (vreq)
929 {
930 /* drms_getkey_string() allocates mem. */
931 free(vreq);
932 vreq = NULL;
933 }
934
935 if (errOut)
936 {
937 return 1;
938 }
939
940 return 0;
941 }
942
943 static int check_and_set_key_time(DRMS_Record_t *outRec, const char *key, TIME tval)
944 {
945 DRMS_Keyword_t *keywd = NULL;
946 TIME treq;
947 int status;
948 char sreq[64], sval[64];
949
950 if (!(keywd = drms_keyword_lookup(outRec, key, 0))) return 1;
951
952 if (!drms_keyword_isconstant(keywd))
953 {
954 /* it's not a constant, so don't worry, just set it */
955 return (drms_setkey_time(outRec, key, tval) != DRMS_SUCCESS);
956 }
957
958 treq = drms_getkey_time(outRec, key, &status);
959 if (status)
960 {
961 fprintf(stderr, "Error retrieving value for constant keyword %s\n", key);
962 return 1;
963 }
964
965 sprint_time(sreq, treq, keywd->info->unit, atoi(keywd->info->format));
966 sprint_time(sval, tval, keywd->info->unit, atoi(keywd->info->format));
967
968 if (strcmp(sval, sreq))
969 {
970 fprintf (stderr, "Error: value %s for constant keyword %s\n", sval, key);
971 fprintf (stderr, " differs from existing value %s\n", sreq);
972 return 1;
973 }
974
975 return 0;
976 }
977
978 static int check_and_set_key_int(DRMS_Record_t *outRec, const char *key, int val)
979 {
980 DRMS_Keyword_t *keywd = NULL;
981 int vreq;
982 int status;
983
984 if (!(keywd = drms_keyword_lookup(outRec, key, 0))) return 1;
985
986 if (!drms_keyword_isconstant(keywd))
987 {
988 /* it's not a constant, so don't worry, just set it */
989 return (drms_setkey_int(outRec, key, val) != DRMS_SUCCESS);
990 }
991
992 vreq = drms_getkey_int(outRec, key, &status);
993 if (status)
994 {
995 fprintf (stderr, "Error retrieving value for constant keyword %s\n", key);
996 return 1;
997 }
998
999 if (vreq != val)
1000 {
1001 char format[256];
1002
1003 snprintf (format, sizeof(format), "Error: new value \"%s\" for constant keyword %%s\n differs from the existing value \"%s\"\n", keywd->info->format, keywd->info->format);
1004 fprintf(stderr, format, val, key, vreq);
1005 return 1;
1006 }
1007
1008 return 0;
1009 }
1010
1011 static int check_and_set_key_float(DRMS_Record_t *outRec, const char *key, float val)
1012 {
1013 DRMS_Keyword_t *keywd = NULL;
1014 float vreq;
1015 int status;
1016 char sreq[128], sval[128];
1017
1018 if (!(keywd = drms_keyword_lookup(outRec, key, 0))) return 1;
1019
1020 if (!drms_keyword_isconstant(keywd))
1021 {
1022 /* it's not a constant, so don't worry, just set it */
1023 return (drms_setkey_float(outRec, key, val) != DRMS_SUCCESS);
1024 }
1025
1026 vreq = drms_getkey_float(outRec, key, &status);
1027
1028 if (status)
1029 {
1030 fprintf (stderr, "Error retrieving value for constant keyword %s\n", key);
1031 return 1;
1032 }
1033
1034 sprintf(sreq, keywd->info->format, vreq);
1035 sprintf(sval, keywd->info->format, val);
1036
1037 if (strcmp(sreq, sval))
1038 {
1039 char format[256];
1040
1041 snprintf(format, sizeof(format), "Error: parameter value %s for keyword %%s\n differs from required value %s\n", keywd->info->format, keywd->info->format);
1042 fprintf(stderr, format, val, key, vreq);
1043 return 1;
1044 }
1045
1046 return 0;
1047 }
1048
1049 int DoIt (void) {
1050 CmdParams_t *params = &cmdparams;
1051 DRMS_RecordSet_t *ids, *ods;
1052 DRMS_Record_t *inrec, *orec;
1053 DRMS_Segment_t *inseg = NULL;
1054 DRMS_Segment_t *oseg = NULL;
1055 DRMS_Array_t *image = NULL, *map = NULL;
1056 int irec;
1057 double *maplat, *maplon, *map_coslat, *map_sinlat;
1058 double x, y, x0, y0, xstp, ystp, xrot, yrot;
1059 double lat, lon, cos_phi, sin_phi;
1060 double img_lat, img_lon;
1061 double img_xscl, img_yscl, img_xc, img_yc, img_radius, img_pa;
1062 double grid_width;
1063 double ellipse_e, ellipse_pa;
1064 float *data;
1065 int axes[2];
1066 int pixct;
1067 int recCount;
1068 int kstat, status;
1069 int need_ephem;
1070 int x_invrt, y_invrt;
1071 int n, col, row;
1072 int MDI_correct, MDI_correct_distort;
1073 unsigned char *offsun, *ongrid;
1074 char source[DRMS_MAXQUERYLEN], recid[DRMS_MAXQUERYLEN];
1075 char module_ident[64], key[64];
1076
1077 double raddeg = M_PI / 180.0;
1078 double degrad = 1.0 / raddeg;
1079 int scaling_override = 0;
1080 char *mapname[] = {"PlateCarree", "Cassini-Soldner", "Mercator",
1081 "LambertCylindrical", "Sanson-Flamsteed", "gnomonic", "Postel",
1082 "stereographic", "orthographic", "LambertAzimuthal"};
1083 char *interpname[] = {"Cubic Convolution", "Nearest Neighbor", "Bilinear"};
1084 char *wcscode[] = {"CAR", "CAS", "MER", "CEA", "GLS", "TAN", "ARC", "STG",
1085 "SIN", "ZEA"};
1086 missing_val = 0.0 / 0.0;
1087 float bblank = -1.0 / 0.0;
1088 float wblank = 1.0 / 0.0;
1089 /* process command params */
1090 const char *inset = params_get_str (params, "in");
1091 char *outser = strdup (params_get_str (params, "out"));
1092 double clat = params_get_double (params, "clat") * raddeg;
1093 double clon = params_get_double (params, "clon") * raddeg;
1094 double map_scale = params_get_double (params, "scale");
1095 double map_pa = params_get_double (params, "map_pa") * raddeg;
1096 float bscaleIn = params_get_float (params, "bscale");
1097 float bzeroIn = params_get_float (params, "bzero");
1098 float grid_spacing = params_get_float (params, "grid");
1099 int map_cols = params_get_int (params, "cols");
1100 int map_rows = params_get_int (params, "rows");
1101 int proj = params_get_int (params, "map");
1102 int intrpopt = params_get_int (params, "interp");
1103 char *RequestID = strdup (params_get_str (params, "requestid")); // XXX PHS
1104 char *clon_key = strdup (params_get_str (params, "clon_key"));
1105 char *clat_key = strdup (params_get_str (params, "clat_key"));
1106 char *rsun_key = strdup (params_get_str (params, "rsun_key"));
1107 char *apsd_key = strdup (params_get_str (params, "apsd_key"));
1108 char *dsun_key = strdup (params_get_str (params, "dsun_key"));
1109 int center = params_isflagset (params, "c");
1110 int stonyhurst = params_isflagset (params, "s");
1111 int verbose = params_isflagset (params, "v");
1112 // XXX PHS added -x
1113 int want_headers = params_isflagset (params, "x");
1114 int overlay = (isfinite (grid_spacing));
1115 int MDI_proc = params_isflagset (params, "M");
1116
1117 int hasSegList = 0;
1118 int doingAllSegs = params_isflagset(params, "A");
1119 float bscale;
1120 float bzero;
1121
1122 snprintf (module_ident, sizeof(module_ident), "%s v %s", module_name, version_id);
1123 if (verbose) printf ("%s: JSOC version %s\n", module_ident, jsoc_version);
1124 /* check calling parameters */
1125 if (map_cols < 1) map_cols = map_rows;
1126 if (map_rows < 1) map_rows = map_cols;
1127 if (map_rows < 1) {
1128 fprintf (stderr, "Error: at least one of \"cols\" or \"rows\" must be set\n");
1129 return 1;
1130 }
1131 if (isnan (map_scale) || map_scale == 0.0) {
1132 fprintf (stderr,
1133 "Error: auto scaling from image resolution not implemented;\n");
1134 fprintf (stderr, " scale parameter must be set.\n");
1135 return 1;
1136 }
1137 MDI_correct = MDI_correct_distort = MDI_proc;
1138 cos_phi = cos (map_pa);
1139 sin_phi = sin (map_pa);
1140 xstp = ystp = map_scale * raddeg;
1141 x0 = 0.5 * (1.0 - map_cols) * xstp;
1142 y0 = 0.5 * (1.0 - map_rows) * ystp;
1143 grid_width = 0.01 * grid_spacing;
1144
1145 char *testSegList = NULL;
1146 char *pCh = NULL;
1147
1148 testSegList = rindex(inset, '}');
1149 if (testSegList)
1150 {
1151 for (pCh = testSegList + 1, hasSegList = 1; *pCh; pCh++)
1152 {
1153 if (!isspace(*pCh))
1154 {
1155 /* There is a non-ws char after '}' - this is not a valid seglist. drms_open_records() will fail below. */
1156 hasSegList = 0;
1157 break;
1158 }
1159 }
1160 }
1161
1162 /* check input */
1163 if (!(ids = drms_open_records(drms_env, inset, &status)))
1164 {
1165 fprintf (stderr, "Error: (%s) unable to open input data set \"%s\"\n", module_ident, inset);
1166 fprintf (stderr, " status = %d\n", status);
1167 return 1;
1168 }
1169
1170 recCount = ids->n;
1171
1172 if (recCount < 1)
1173 {
1174 fprintf(stderr, "Error: (%s) no records in selected input set\n", module_ident);
1175 fprintf(stderr, " %s\n", inset);
1176 drms_close_records(ids, DRMS_FREE_RECORD);
1177 return 1;
1178 }
1179
1180 /* open output record set */
1181 if (verbose) printf ("creating %d records in series %s\n", recCount, outser);
1182 ods = drms_create_records(drms_env, recCount, outser, DRMS_PERMANENT, &status);
1183 if (!ods || status != DRMS_SUCCESS)
1184 {
1185 fprintf (stderr, "Error: unable to create %d records in series %s\n", recCount, outser);
1186 fprintf (stderr, " drms_create_records() returned status %d\n", status);
1187 return 1;
1188 }
1189
1190 /* determine if output series is <input series>_mod as used for processing on export
1191 from exportdata.html */
1192 char *runderscore = rindex(outser, '_');
1193 int running_as_export = (runderscore && strcmp(runderscore, "_mod")==0);
1194
1195 /* create output map array */
1196 axes[0] = map_cols;
1197 axes[1] = map_rows;
1198 map = drms_array_create (DRMS_TYPE_FLOAT, 2, axes, NULL, &status);
1199 if (status) {
1200 fprintf (stderr, "Error: couldn't create output array\n");
1201 return 1;
1202 }
1203 pixct = map_cols * map_rows;
1204 maplat = (double *)malloc (pixct * sizeof (double));
1205 maplon = (double *)malloc (pixct * sizeof (double));
1206 map_coslat = (double *)malloc (pixct * sizeof (double));
1207 map_sinlat = (double *)malloc (pixct * sizeof (double));
1208 offsun = (unsigned char *)malloc (pixct * sizeof (char));
1209 if (overlay) ongrid = (unsigned char *)malloc (pixct * sizeof (char));
1210 /* use output series default segment scaling if not overridden */
1211
1212
1213 /* Calculate heliographic coordinates corresponding to map location(s) */
1214 for (n=0, row=0, y=y0; row < map_rows; row++, y += ystp)
1215 {
1216 for (col=0, x=x0; col < map_cols; col++, x += xstp, n++)
1217 {
1218 xrot = x * cos_phi - y * sin_phi;
1219 yrot = y * cos_phi + x * sin_phi;
1220 offsun[n] = plane2sphere (xrot, yrot, clat, clon, &lat, &lon, proj);
1221 maplat[n] = lat;
1222 maplon[n] = lon;
1223 map_coslat[n] = cos (lat);
1224 map_sinlat[n] = sin (lat);
1225 if (overlay) ongrid[n] = near_grid_line (maplon[n], maplat[n], grid_spacing, grid_width);
1226 }
1227 }
1228
1229 data = (float *)map->data;
1230
1231
1232
1233
1234 /* process individual input mages */
1235
1236 /* This is really a loop over records (wherein it is assumed that there is a single image
1237 * per record). */
1238 for (irec = 0; irec < recCount; irec++)
1239 {
1240 /* get input image */
1241 inrec = ids->records[irec];
1242 drms_sprint_rec_query(source, inrec);
1243
1244 if (drms_record_numsegments(inrec) < 1)
1245 {
1246 fprintf(stderr, "Error: no data segments in input record-set %s\n", inset);
1247 drms_close_records(ids, DRMS_FREE_RECORD);
1248 return 1;
1249 }
1250
1251 fprintf(stderr, "Maproj reading rec %d\n", irec);
1252
1253 /* Segment loop */
1254 DRMS_Segment_t *firstSeg = NULL;
1255 HIterator_t *segIter = NULL;
1256 DRMS_Segment_t *orig = NULL;
1257 int validSegsCount;
1258
1259 validSegsCount = 0;
1260 while ((inseg = drms_record_nextseg2(inrec, &segIter, 1, &orig)) != NULL)
1261 {
1262 if (!firstSeg)
1263 {
1264 firstSeg = inseg;
1265 }
1266 else
1267 {
1268 if (!hasSegList && !doingAllSegs)
1269 {
1270 /* We are processing only the first segment of each record. */
1271 break;
1272 }
1273 }
1274
1275 /* Exclude segments that do not meet the requirements of this program. */
1276 if (drms_segment_getnaxis(inseg) == 2)
1277 {
1278 ++validSegsCount;
1279 }
1280 else
1281 {
1282 continue;
1283 }
1284
1285 if (validSegsCount == 0)
1286 {
1287 fprintf (stderr, "Error: no data segment of dimension 2 in input series %s\n", inset);
1288 drms_close_records(ids, DRMS_FREE_RECORD);
1289 drms_close_records(ods, DRMS_FREE_RECORD);
1290 return 1;
1291 }
1292
1293 image = drms_segment_read(inseg, DRMS_TYPE_FLOAT, &status);
1294 /* get needed info from record keys for mapping */
1295 /* replace with call to solar_ephemeris_info? */
1296
1297 if (!image || status != DRMS_SUCCESS)
1298 {
1299 fprintf(stderr, "Unable to read input segment (%s).\n", inseg->info->name);
1300 drms_close_records(ids, DRMS_FREE_RECORD);
1301 drms_close_records(ods, DRMS_FREE_RECORD);
1302 return 1;
1303 }
1304
1305 img_lon = drms_getkey_double(inrec, clon_key, &status);
1306 img_lat = drms_getkey_double(inrec, clat_key, &status);
1307
1308 status = solar_image_info(inrec, &img_xscl, &img_yscl, &img_xc, &img_yc, &img_radius, rsun_key, apsd_key, &img_pa, &ellipse_e, &ellipse_pa, &x_invrt, &y_invrt, &need_ephem, 0);
1309 if (status & NO_SEMIDIAMETER)
1310 {
1311 int keystat = 0;
1312 double dsun_obs = drms_getkey_double(inrec, dsun_key, &keystat);
1313
1314 if (keystat)
1315 {
1316 fprintf (stderr, "Error: one or more essential keywords or values missing, needed %s; skipped\n",dsun_key);
1317 fprintf (stderr, "solar_image_info() returned %08x\n", status);
1318 continue;
1319 }
1320 /* set image radius from scale and distance */
1321 img_radius = asin (RSUNM / dsun_obs);
1322 img_radius *= 3600.0 * degrad;
1323 img_radius /= (img_xscl <= img_yscl) ? img_xscl : img_yscl;
1324 status &= ~NO_SEMIDIAMETER;
1325 }
1326
1327 if (status == KEYSCOPE_VARIABLE)
1328 {
1329 fprintf(stderr, "Warning: one or more keywords expected constant are variable\n");
1330 }
1331 else if (status)
1332 {
1333 fprintf(stderr, "Warning: one or more essential keywords or values missing, 2nd message; skipped\n");
1334 fprintf(stderr, "solar_image_info() returned %08x\n", status);
1335 continue;
1336 }
1337
1338 if (MDI_correct)
1339 {
1340 mtrack_MDI_correct_imgctr(&img_xc, &img_yc, img_radius);
1341 mtrack_MDI_correct_pa(&img_pa);
1342 }
1343
1344 img_xc -= 0.5 * (image->axis[0] - 1);
1345 img_yc -= 0.5 * (image->axis[1] - 1);
1346 /* should be taken care of in solar_ephemeris_info */
1347 img_lon *= raddeg;
1348 img_lat *= raddeg;
1349
1350 /* set up output record */
1351 orec = ods->records[irec];
1352 oseg = drms_segment_lookupnum(orec, orig->info->segnum);
1353
1354
1355 /* All of this code is segment-dependent. It used to reside outside any loop
1356 * (before the record loop). It was moved here, inside the segment loop. The
1357 * map array was allocated outside any loop, and freed outside any loop. */
1358 scaling_override = 0;
1359 bscale = bscaleIn;
1360 if (bscaleIn == 0.0)
1361 {
1362 bscale = oseg->bscale;
1363 if (verbose) printf ("bscale set to output default: %g\n", bscale);
1364 }
1365 else
1366 {
1367 scaling_override = 1;
1368 }
1369
1370 bzero = bzeroIn;
1371 if (isnan (bzero))
1372 {
1373 bzero = oseg->bzero;
1374 if (verbose) printf ("bzero set to output default: %g\n", bzero);
1375 }
1376 else
1377 {
1378 scaling_override = 1;
1379 }
1380
1381 map->bscale = bscale;
1382 map->bzero = bzero;
1383 /* End moved scaling code. */
1384
1385 /* perform the mapping */
1386 perform_mapping(image, data, maplat, maplon, map_coslat, map_sinlat, pixct, offsun, img_lat, img_lon, img_xc, img_yc, img_radius, img_pa, ellipse_e, ellipse_pa, x_invrt, y_invrt, intrpopt, MDI_correct_distort);
1387
1388 /* We are guaranteed that image is not NULL at this point. */
1389 drms_free_array(image); // PHS
1390
1391 if (overlay)
1392 {
1393 for (n = 0; n < pixct; n++)
1394 {
1395 if (ongrid[n]) data[n] = (isfinite(data[n])) ? bblank : wblank;
1396 }
1397 }
1398
1399 // XXX PHS moved segment write to end to allow writing with headers.
1400
1401 /* set output record key values */
1402 // XXX PHS different copykeys flag, do statistics after copykeys.
1403 drms_copykeys(orec, inrec, 0, kDRMS_KeyClass_Explicit); // XXX PHS
1404 drms_setkey_string(orec, "requestid", RequestID); // XXX PHS
1405 set_statistics(oseg, map, 1); // XXX PHS
1406
1407 kstat = 0;
1408 kstat += check_and_set_key_str (orec, "WCSNAME", "Carrington Heliographic");
1409 kstat += check_and_set_key_int (orec, "WCSAXES", 2);
1410 snprintf (key, sizeof(key), "CRLN-%s", wcscode[proj]);
1411 kstat += check_and_set_key_str (orec, "CTYPE1", key);
1412 snprintf (key, sizeof(key), "CRLT-%s", wcscode[proj]);
1413 kstat += check_and_set_key_str (orec, "CTYPE2", key);
1414 kstat += check_and_set_key_str (orec, "CUNIT1", "deg");
1415 kstat += check_and_set_key_str (orec, "CUNIT2", "deg");
1416 kstat += check_and_set_key_float (orec, "CRPIX1", 0.5 * map_cols + 0.5);
1417 kstat += check_and_set_key_float (orec, "CRPIX2", 0.5 * map_rows + 0.5);
1418 kstat += check_and_set_key_float (orec, "CRVAL1", clon * degrad);
1419 kstat += check_and_set_key_float (orec, "CRVAL2", clat * degrad);
1420 kstat += check_and_set_key_float (orec, "CDELT1", map_scale);
1421 kstat += check_and_set_key_float (orec, "CDELT2", map_scale);
1422 // XXX PHS, CROTA2 will be different after projection.
1423 kstat += check_and_set_key_float (orec, "CROTA2", map_pa * degrad);
1424 if (map_pa != 0.0) {
1425 kstat += check_and_set_key_float (orec, "PC1_1", cos (map_pa));
1426 /* PC1_2 should be multiplied by CDELT2 / CDELT1 */
1427 kstat += check_and_set_key_float (orec, "PC1_2", sin (map_pa));
1428 /* PC2_1 should be multiplied by CDELT1 / CDELT2 */
1429 kstat += check_and_set_key_float (orec, "PC2_1", sin (map_pa));
1430 kstat += check_and_set_key_float (orec, "PC2_2", cos (map_pa));
1431 }
1432
1433 kstat += check_and_set_key_float (orec, "LonHG", clon * degrad);
1434 kstat += check_and_set_key_float (orec, "LatHG", clat * degrad);
1435 kstat += check_and_set_key_str (orec, "MapProj", mapname[proj]);
1436 kstat += check_and_set_key_float (orec, "MapScale", map_scale);
1437 kstat += check_and_set_key_float (orec, "Width", map_cols * map_scale);
1438 kstat += check_and_set_key_float (orec, "Height", map_rows * map_scale);
1439 kstat += check_and_set_key_float (orec, "Size", sqrt (map_rows * map_cols) * map_scale);
1440 kstat += check_and_set_key_float (orec, "Map_PA", map_pa / raddeg);
1441 kstat += check_and_set_key_float (orec, "RSunRef", 1.0e-6 * RSUNM);
1442 kstat += check_and_set_key_str (orec, "Interp", interpname[intrpopt]);
1443
1444 kstat += check_and_set_key_str (orec, "Module", module_ident);
1445 kstat += check_and_set_key_str (orec, "BLD_VERS", jsoc_version);
1446 kstat += check_and_set_key_time (orec, "Created", CURRENT_SYSTEM_TIME);
1447 kstat += check_and_set_key_str (orec, "Source", source);
1448 kstat += check_and_set_key_str (orec, "Input", inset);
1449
1450 // XXX PHS if running_as_export ignore setkey failures and add HISTORY for mapping params since
1451 // input_series"_mod" will not have the maproj specific keywords.
1452 if (running_as_export)
1453 {
1454 char buf[1024];
1455
1456 drms_appendhistory(orec, "MapProj=", 1); drms_appendhistory(orec, mapname[proj], 0);
1457 sprintf(buf, "LonHG=%f, LatHG=%f", clon * degrad, clat * degrad);
1458 drms_appendhistory(orec, buf, 1);
1459 drms_appendhistory(orec, "Interp=", 1); drms_appendhistory(orec, interpname[intrpopt], 0);
1460 if (overlay)
1461 {
1462 sprintf(buf, "%f", grid_spacing);
1463 drms_appendhistory(orec, "GridSpacing=", 1); drms_appendhistory(orec, buf, 0);
1464 }
1465 drms_setkey_time(orec, "DATE", CURRENT_SYSTEM_TIME);
1466 drms_appendhistory(orec, "Source=", 1); drms_appendhistory(orec, source, 0);
1467 }
1468 else if (kstat)
1469 {
1470 fprintf (stderr, "Error writing key value(s) to record %d in series %s\n", irec, outser);
1471 fprintf (stderr, " series may not have appropriate structure\n");
1472 drms_close_records(ids, DRMS_FREE_RECORD);
1473 drms_close_records(ods, DRMS_FREE_RECORD);
1474 return 1;
1475 }
1476
1477 // XXX PHS if scaling not given use input segment for bscale and bzero.
1478 if (!scaling_override)
1479 {
1480 map->bscale = inseg->bscale;
1481 if (verbose) printf("bscale set to input record value: %g\n", bscale);
1482 map->bzero = inseg->bzero;
1483 if (verbose) printf("bzero set to input record value: %g\n", bzero);
1484 }
1485
1486 // XXX PHS moved segment write to here to allow writing with headers.
1487 /* write map array to output record segment */
1488 if (want_headers)
1489 {
1490 status = drms_segment_writewithkeys(oseg, map, 0);
1491 }
1492 else
1493 {
1494 status = drms_segment_write(oseg, map, 0);
1495 }
1496
1497 if (status)
1498 {
1499 drms_sprint_rec_query (recid, orec);
1500 fprintf (stderr, "Error writing data to record %s\n", recid);
1501 fprintf (stderr, " series may not have appropriate structure\n");
1502 drms_close_records(ids, DRMS_FREE_RECORD);
1503 drms_close_records(ods, DRMS_FREE_RECORD);
1504 return 1;
1505 }
1506 } /* End segment loop */
1507
1508 if (segIter)
1509 {
1510 hiter_destroy(&segIter);
1511 }
1512
1513 if (!firstSeg)
1514 {
1515 /* Never found a segment for this record.*/
1516 fprintf(stderr, "Error: no data segment of dimension 2 in input record-set %s\n", inset);
1517 drms_close_records(ids, DRMS_FREE_RECORD);
1518 drms_close_records(ods, DRMS_FREE_RECORD);
1519 return 1;
1520 }
1521 } /* End record loop. */
1522
1523 drms_close_records(ods, DRMS_INSERT_RECORD);
1524 drms_free_array(map);
1525 return 0;
1526 }