1 |
/** |
2 |
@defgroup jsoc_rebin jsoc_rebin reduce/increase image size by integer multiples |
3 |
@ingroup su_util |
4 |
|
5 |
@brief Reduce (increase) the dimension of the input data by an integer factor. |
6 |
|
7 |
@par Synopsis: |
8 |
@code |
9 |
jsoc_rebin in=input_data out=output_data scale=<scale> method={boxcar,gaussian} |
10 |
where scale is <1 for size reduction and >1 for size increase. The output image scale |
11 |
will be the nearest integer to "scale" or its reciprocal for scale < 1.0. |
12 |
@endcode |
13 |
|
14 |
This is a general purpose module that takes a series of input |
15 |
data and modifies its spatial size by a factor |
16 |
specified by "scale". |
17 |
The method for avaraging (interpolation) can be specified |
18 |
through the input "method". The current version handles a simple |
19 |
boxcar average and Gaussian filtered sampling. If 'scale' < 1 then the input is reduced in size. |
20 |
|
21 |
The image is not registered to solar center by this module. The image will be rotated by |
22 |
a flip-flip procedure is the CROTA2 parameter is near +-180.0 unless the -u flag (unchanged) is |
23 |
present. If the -c flag is present the image will be cropped at the solar limb before scaling. |
24 |
If the -h flag or requestid parameter is present the output segments will have full headers. |
25 |
|
26 |
If gaussian smoothing is specified (via method) then the FWHM and nvector parameters should also |
27 |
be provided. These define the Gaussian smoothing vector. nvector is the full width of the |
28 |
smoothing function. nvector will be adjusted such that nvector is odd if 1/scale rounds to an odd integer. |
29 |
|
30 |
@par Flags: |
31 |
@c |
32 |
-A Process all segments |
33 |
-c Crop before scaling. Use rsun_obs/cdelt1 for limb radius. |
34 |
-h Write full FITS headers. |
35 |
-u Leave unchanged, do NOT rotate by 180 degrees if CROTA2 is near 180. default is to do flip-flip method so |
36 |
image is norths up and no pixel values are changed. |
37 |
|
38 |
@par GEN_FLAGS: |
39 |
Ubiquitous flags present in every module. |
40 |
@ref jsoc_main |
41 |
|
42 |
@param in The input data series. |
43 |
@param out The output series. |
44 |
|
45 |
@par Exit_Status: |
46 |
|
47 |
@par Example: |
48 |
Takes a series of 1024 X 1024 MDI full disk magnetogram and |
49 |
produces images with the resolution of HMI, 4096 X 4096. |
50 |
|
51 |
@code |
52 |
jsoc_rebin in='mdi.fd_M_96m_lev18[2003.10.20/1d]' out='su_phil.mdi_M_4k' scale=4 method='boxcar' |
53 |
@endcode |
54 |
|
55 |
@par Example: |
56 |
Reduces the resolution of HMI images 4096 X 4096 to that of MDI, |
57 |
1024 X 1024. Here the input is the HMI images and the output |
58 |
is the lower resolution HMI images. Crop and rotate before rescaling. |
59 |
@code |
60 |
jsoc_rebin -c in=hmi.M_45s[2011.10.20/1d]' out='su_phil.hmi_M_1k_45s' scale=0.25 method='boxcar' |
61 |
@endcode |
62 |
|
63 |
@bug |
64 |
None known so far. |
65 |
|
66 |
*/ |
67 |
|
68 |
#include "jsoc.h" |
69 |
#include "jsoc_main.h" |
70 |
#include "fstats.h" |
71 |
|
72 |
char *module_name = "jsoc_rebin"; |
73 |
|
74 |
#define DIE(msg) {fflush(stdout);fprintf(stderr,"%s, status=%d\n",msg,status); return(status);} |
75 |
|
76 |
ModuleArgs_t module_args[] = |
77 |
{ |
78 |
{ARG_STRING, "in", "NOT SPECIFIED", "Input data series."}, |
79 |
{ARG_STRING, "out", "NOT SPECIFIED", "Output data series."}, |
80 |
{ARG_FLAG, "A", "0", "Process all segments in the input series"}, |
81 |
{ARG_FLAG, "c", "0", "Crop at rsun_obs."}, |
82 |
{ARG_FLAG, "h", "0", "Include full FITS header in output segment."}, |
83 |
{ARG_FLAG, "u", "0", "do not rotate by 180 if needed."}, |
84 |
{ARG_FLOAT, "scale", "1.0", "Scale factor."}, |
85 |
{ARG_FLOAT, "FWHM", "-1.0", "Smoothing Gaussian FWHM for method=gaussian."}, |
86 |
{ARG_INT, "nvector", "-1.0", "Smoothing Gaussian vector length for method=gaussian."}, |
87 |
{ARG_INT, "inseg", "-1", "Input segment number"}, |
88 |
{ARG_INT, "outseg", "-1", "Output segment number"}, |
89 |
{ARG_STRING, "method", "boxcar", "conversion type, one of: boxcar, gaussian."}, |
90 |
{ARG_STRING, "requestid", "NA", "RequestID if called as an export processing step."}, |
91 |
{ARG_END} |
92 |
}; |
93 |
|
94 |
#define Deg2Rad (M_PI/180.0) |
95 |
#define Rad2arcsec (3600.0/Deg2Rad) |
96 |
#define arcsec2Rad (Deg2Rad/3600.0) |
97 |
#define Rad2Deg (180.0/M_PI) |
98 |
|
99 |
struct ObsInfo_struct |
100 |
{ |
101 |
// from observation info |
102 |
TIME t_obs; |
103 |
double rsun_obs, obs_vr, obs_vw, obs_vn; |
104 |
double crpix1, crpix2, cdelt1, cdelt2, crota2; |
105 |
double crval1, crval2; |
106 |
double cosa, sina; |
107 |
double obs_b0; |
108 |
// observed point |
109 |
int i,j; |
110 |
// parameters for observed point |
111 |
double x,y,r; |
112 |
double rho; |
113 |
double lon; |
114 |
double lat; |
115 |
double sinlat, coslat; |
116 |
double sig; |
117 |
double mu; |
118 |
double chi; |
119 |
double obs_v; |
120 |
}; |
121 |
|
122 |
typedef struct ObsInfo_struct ObsInfo_t; |
123 |
|
124 |
void rebinArraySF(DRMS_Array_t *out, DRMS_Array_t *in); |
125 |
int upNcenter(DRMS_Array_t *arr, ObsInfo_t *ObsLoc); |
126 |
int crop_image(DRMS_Array_t *arr, ObsInfo_t *ObsLoc); |
127 |
const char *get_input_recset(DRMS_Env_t *drms_env, const char *in); |
128 |
|
129 |
ObsInfo_t *GetObsInfo(DRMS_Segment_t *seg, ObsInfo_t *pObsLoc, int *rstatus); |
130 |
ObsInfo_t *GetMinObsInfo(DRMS_Segment_t *seg, ObsInfo_t *pObsLoc, int *rstatus); |
131 |
|
132 |
int DoIt(void) |
133 |
{ |
134 |
int status = DRMS_SUCCESS; |
135 |
DRMS_RecordSet_t *inRS, *outRS; |
136 |
int irec, nrecs; |
137 |
const char *inQuery = params_get_str(&cmdparams, "in"); |
138 |
char *inStr; |
139 |
const char *outSeries = params_get_str(&cmdparams, "out"); |
140 |
const char *method = params_get_str(&cmdparams, "method"); |
141 |
const char *requestid = params_get_str(&cmdparams, "requestid"); |
142 |
int nvector = params_get_int(&cmdparams, "nvector"); |
143 |
float fscale = params_get_float(&cmdparams, "scale"); |
144 |
float fwhm = params_get_float(&cmdparams, "FWHM"); |
145 |
int crop = params_get_int(&cmdparams, "c"); |
146 |
int as_is = params_get_int(&cmdparams, "u"); |
147 |
int wantAllSegs = params_get_int(&cmdparams, "A"); |
148 |
int inseg = params_get_int(&cmdparams, "inseg"); |
149 |
int outseg = params_get_int(&cmdparams, "outseg"); |
150 |
int full_header = params_get_int(&cmdparams, "h") || strcmp(requestid, "NA"); |
151 |
char *in_filename = NULL; |
152 |
DRMS_Record_t *template = NULL; |
153 |
int firstNx = 0, firstNy = 0, firstNaxis = 0; |
154 |
int wantSeg = 1; |
155 |
|
156 |
int iscale, ivec, vec_half; |
157 |
double *vector; |
158 |
char history[4096]; |
159 |
|
160 |
if (fscale < 1.0) // shrinking |
161 |
iscale = 1.0/fscale + 0.5; |
162 |
else // enlarging |
163 |
iscale = fscale + 0.5; |
164 |
if (nvector < 0) |
165 |
nvector = iscale; |
166 |
// Both 1/scale and nvector must be odd or both even so add 1 to nvector if needed |
167 |
if (((iscale & 1) && !(nvector & 1)) || ((!(iscale & 1) && (nvector & 1) ))) |
168 |
nvector += 1; |
169 |
vector = (double *)malloc(nvector * sizeof(double)); |
170 |
vec_half = nvector/2; // counts on truncate to int if nvector is odd. |
171 |
|
172 |
if (strcasecmp(method, "boxcar")==0 && fscale < 1) |
173 |
{ |
174 |
for (ivec = 0; ivec < nvector; ivec++) |
175 |
vector[ivec] = 1.0; |
176 |
sprintf(history, "Boxcar bin by %d%s%s", |
177 |
iscale, |
178 |
(crop ? ", Cropped at rsun_obs" : ""), |
179 |
(!as_is ? ", North is up" : "") ); |
180 |
} |
181 |
else if (strcasecmp(method, "boxcar")==0 && fscale >= 1) |
182 |
{ |
183 |
if (nvector != iscale) |
184 |
DIE("For fscale>=1 nvector must be fscale"); |
185 |
for (ivec = 0; ivec < nvector; ivec++) |
186 |
vector[ivec] = 1.0; |
187 |
sprintf(history, "Replicate to expand by %d%s%s", |
188 |
iscale, |
189 |
(crop ? ", Cropped at rsun_obs" : ""), |
190 |
(!as_is ? ", North is up" : "") ); |
191 |
} |
192 |
else if (strcasecmp(method, "gaussian")==0) // do 2-D vector weights calculated as Gaussian |
193 |
{ |
194 |
if (fwhm < 0) |
195 |
DIE("Need FWHM parameter"); |
196 |
for (ivec = 0; ivec < nvector; ivec++) |
197 |
{ |
198 |
double arg = (ivec - (nvector-1)/2.0) * (ivec - (nvector-1)/2.0); |
199 |
vector[ivec] = exp(-arg/(fwhm*fwhm*0.52034)); |
200 |
} |
201 |
sprintf(history, "Scale by %f with Gasussian smoothing FWHM=%f, nvector=%d%s%s", |
202 |
fscale, fwhm, nvector, |
203 |
(crop ? ", Cropped at rsun_obs" : ""), |
204 |
(!as_is ? ", North is up" : "") ); |
205 |
} |
206 |
else |
207 |
DIE("invalid conversion method"); |
208 |
|
209 |
printf(history); |
210 |
|
211 |
inStr = strdup(get_input_recset(drms_env, (char *)inQuery)); |
212 |
if (!inStr || *inStr=='\0') DIE("Cant make special cadence recordset list file"); |
213 |
inRS = drms_open_records(drms_env, inStr, &status); |
214 |
if (strcmp(inStr, inQuery) && *inStr == '@') |
215 |
unlink(inStr+1); |
216 |
if (status || inRS->n == 0) |
217 |
DIE("No input data found"); |
218 |
|
219 |
nrecs = inRS->n; |
220 |
if (nrecs == 0) |
221 |
DIE("No records found"); |
222 |
drms_stage_records(inRS, 1, 1); |
223 |
|
224 |
outRS = drms_create_records(drms_env, nrecs, (char *)outSeries, DRMS_PERMANENT, &status); |
225 |
if (status) |
226 |
DIE("Output recordset not created"); |
227 |
|
228 |
int iSet; |
229 |
int iSubRec; |
230 |
int nSubRecs = 0; |
231 |
|
232 |
/* We need the record-set specfication for each record, so loop by recordset subset. */ |
233 |
for (iSet = 0, irec = 0; iSet < inRS->ss_n; iSet++) |
234 |
{ |
235 |
int hasSeglist = wantAllSegs ? 1 : 0; |
236 |
|
237 |
/* For each record, we need to know if the segments it contains came from a record-set specification that had a segment list. |
238 |
* To do that, we have to search for '{' in the specification that resolved to this record. So we need the spec for this record, |
239 |
* which is in inRS->ss_queries[iSet]. */ |
240 |
if (strchr(inRS->ss_queries[iSet], '{')) |
241 |
{ |
242 |
hasSeglist = 1; |
243 |
} |
244 |
|
245 |
nSubRecs = drms_recordset_getssnrecs(inRS, iSet, &status); |
246 |
|
247 |
for (iSubRec = 0; iSubRec < nSubRecs; iSubRec++, irec++) |
248 |
{ |
249 |
ObsInfo_t *ObsLoc; |
250 |
DRMS_Record_t *outRec, *inRec; |
251 |
DRMS_Segment_t *outSeg, *inSeg; |
252 |
DRMS_Segment_t *orig = NULL; |
253 |
DRMS_Array_t *inArray, *outArray; |
254 |
float *inData, *outData; |
255 |
float val; |
256 |
int quality=0; |
257 |
|
258 |
inRec = inRS->records[(inRS->ss_starts)[iSet] + iSubRec]; |
259 |
|
260 |
quality = drms_getkey_int(inRec, "QUALITY", &status); |
261 |
if (status || (!status && quality >= 0)) |
262 |
{ |
263 |
/* Segment loop. */ |
264 |
HIterator_t *segIter = NULL; |
265 |
|
266 |
while ((inSeg = drms_record_nextseg2(inRec, &segIter, 1, &orig)) != NULL) |
267 |
{ |
268 |
/* A segment can only be processed if it is dimension 2 and if not first segment has same dimension |
269 |
* as first segment. |
270 |
*/ |
271 |
if (inseg >= 0) |
272 |
{ |
273 |
/* This module was called with the inseg argument (which is the input series' segment number). |
274 |
* Process only that segment. But inSeg could be a linked segment, so we have to obtain the |
275 |
* segment struct in the original series. There is no pointer from the linked segment to |
276 |
* the original segment. And we do not have the name or number of the original segment. Or we |
277 |
* didn't, so I made drms_record_nextseg2(). */ |
278 |
if (orig->info->segnum != inseg) |
279 |
{ |
280 |
/* Skip this segment. It isn't the one the caller has requested. */ |
281 |
continue; |
282 |
} |
283 |
} |
284 |
wantSeg = 1; // start by assuming OK |
285 |
|
286 |
inArray = drms_segment_read(inSeg, DRMS_TYPE_FLOAT, &status); |
287 |
if (status) |
288 |
{ |
289 |
printf(" No data file found but QUALITY not bad, status=%d\n", status); |
290 |
drms_free_array(inArray); |
291 |
continue; |
292 |
} |
293 |
int inx, iny, outx, outy, i, j; |
294 |
int in_nx = inArray->axis[0]; |
295 |
int in_ny = inArray->axis[1]; |
296 |
int out_nx = in_nx * fscale + 0.5; |
297 |
int out_ny = in_ny * fscale + 0.5; |
298 |
int outDims[2] = {out_nx, out_ny}; |
299 |
if (firstNaxis == 0) |
300 |
{ |
301 |
if (inArray->naxis != 2) |
302 |
wantSeg = 0; // Skip segment if not 2 axes. mark for skip below |
303 |
else |
304 |
{ |
305 |
firstNaxis = inArray->naxis; |
306 |
firstNx = in_nx; |
307 |
firstNy = in_ny; |
308 |
} |
309 |
} |
310 |
if (inArray->naxis != firstNaxis || in_nx != firstNx || in_ny != firstNy) |
311 |
{ // this segment now wanted, if no segment list provided, copy this segment as-is |
312 |
wantSeg = 0; |
313 |
} |
314 |
|
315 |
if (wantSeg && (crop || !as_is)) |
316 |
{ |
317 |
ObsLoc = GetObsInfo(inSeg, NULL, &status); |
318 |
if (!as_is) upNcenter(inArray, ObsLoc); |
319 |
if (crop) crop_image(inArray, ObsLoc); |
320 |
} |
321 |
else if (wantSeg) |
322 |
{ |
323 |
ObsLoc = GetMinObsInfo(inSeg, NULL, &status); |
324 |
} |
325 |
|
326 |
if (wantSeg) |
327 |
{ |
328 |
inData = (float *)inArray->data; |
329 |
outArray = drms_array_create(DRMS_TYPE_FLOAT, 2, outDims, NULL, &status); |
330 |
outData = (float *)outArray->data; |
331 |
} |
332 |
|
333 |
if (wantSeg && fscale > 1.0) |
334 |
{ |
335 |
int out_go = (iscale-1)/2.0 + 0.5; |
336 |
for (iny = 0; iny < in_ny; iny += 1) |
337 |
for (inx = 0; inx < in_nx; inx += 1) |
338 |
{ |
339 |
val = inData[in_nx*iny + inx]; |
340 |
for (j = 0; j < nvector; j += 1) |
341 |
{ |
342 |
outy = iny*iscale + out_go + j - vec_half; |
343 |
for (i = 0; i < nvector; i += 1) |
344 |
{ |
345 |
outx = inx*iscale + out_go + i - vec_half; |
346 |
if (outx >= 0 && outx < out_nx && outy >= 0 && outy < out_ny) |
347 |
outData[out_nx*outy + outx] = val; |
348 |
} |
349 |
} |
350 |
} |
351 |
} |
352 |
else if (wantSeg) |
353 |
{ |
354 |
int in_go = (iscale-1)/2.0 + 0.5; |
355 |
for (outy = 0; outy < out_ny; outy += 1) |
356 |
for (outx = 0; outx < out_nx; outx += 1) |
357 |
{ |
358 |
double total = 0.0; |
359 |
double weight = 0.0; |
360 |
int nn = 0; |
361 |
for (j = 0; j < nvector; j += 1) |
362 |
{ |
363 |
iny = outy*iscale + in_go + j - vec_half; |
364 |
for (i = 0; i < nvector; i += 1) |
365 |
{ |
366 |
inx = outx*iscale + in_go + i - vec_half; |
367 |
if (inx >= 0 && inx < in_nx && iny >=0 && iny < in_ny) |
368 |
{ |
369 |
val = inData[in_nx*(iny) + inx]; |
370 |
if (!drms_ismissing_float(val)) |
371 |
{ |
372 |
double w = vector[i]*vector[j]; |
373 |
total += w*val; |
374 |
weight += w; |
375 |
nn++; |
376 |
} |
377 |
} |
378 |
} |
379 |
} |
380 |
outData[out_nx*outy + outx] = (nn > 0 ? total/weight : DRMS_MISSING_FLOAT); |
381 |
} |
382 |
} |
383 |
|
384 |
|
385 |
// write data file |
386 |
outRec = outRS->records[irec]; |
387 |
|
388 |
/* The original intent was for the output series' segments to parallel the input series' segments. |
389 |
* However, the names were allowed to vary. So the segment descriptions match, and they are in the |
390 |
* same order. However, the names given to these descriptions might vary. Therefore, we need to |
391 |
* look-up output segments by segment number, not name. |
392 |
* |
393 |
* If outseg was provided in the arguments, use that. Otherwise, use the segment number of the input |
394 |
* segment (the original input segment, in case a link was followed). |
395 |
*/ |
396 |
outSeg = drms_segment_lookupnum(outRec, (outseg >= 0) ? outseg : orig->info->segnum); |
397 |
|
398 |
if (!outSeg) |
399 |
{ |
400 |
DIE("Unable to look-up output segment."); |
401 |
} |
402 |
|
403 |
// copy all keywords |
404 |
drms_copykeys(outRec, inRec, 0, kDRMS_KeyClass_Explicit); |
405 |
|
406 |
// Now fixup coordinate keywords |
407 |
// Only CRPIX1,2 and CDELT1,2 and CROTA2 should need repair. |
408 |
drms_setkey_double(outRec, "CDELT1", ObsLoc->cdelt1/fscale); |
409 |
drms_setkey_double(outRec, "CDELT2", ObsLoc->cdelt2/fscale); |
410 |
drms_setkey_double(outRec, "CRPIX1", (ObsLoc->crpix1-0.5) * fscale + 0.5); |
411 |
drms_setkey_double(outRec, "CRPIX2", (ObsLoc->crpix2-0.5) * fscale + 0.5); |
412 |
drms_setkey_double(outRec, "CROTA2", ObsLoc->crota2); |
413 |
|
414 |
if (strcasecmp(method, "gaussian")==0) |
415 |
drms_setkey_double(outRec, "FWHM", fwhm); |
416 |
drms_appendhistory(outRec, history, 1); |
417 |
drms_setkey_time(outRec, "DATE", CURRENT_SYSTEM_TIME); |
418 |
if (strcmp(requestid, "NA") != 0) |
419 |
drms_setkey_string(outRec, "RequestID", requestid); |
420 |
|
421 |
|
422 |
if (!wantSeg) |
423 |
{ |
424 |
drms_free_array(inArray); |
425 |
inArray = drms_segment_read(inSeg,DRMS_TYPE_RAW,&status); |
426 |
if (!inArray || status) |
427 |
DIE("Problem reading ancillary array"); |
428 |
status = drms_segment_write(outSeg, inArray, 0); |
429 |
if (status) |
430 |
DIE("Problem writing ancillary array"); |
431 |
drms_free_array(inArray); |
432 |
} |
433 |
else |
434 |
{ |
435 |
// get info for array from input segment |
436 |
outArray->parent_segment = outSeg; |
437 |
|
438 |
drms_setkey_int(outRec, "TOTVALS", out_nx*out_ny); |
439 |
set_statistics(outSeg, outArray, 1); |
440 |
|
441 |
// Use the input array as the best guess for scale and zero |
442 |
outArray->bzero = inArray->bzero; |
443 |
outArray->bscale = inArray->bscale; |
444 |
|
445 |
drms_free_array(inArray); |
446 |
if (full_header) |
447 |
status = drms_segment_writewithkeys(outSeg, outArray, 0); |
448 |
else |
449 |
status = drms_segment_write(outSeg, outArray, 0); |
450 |
if (status) |
451 |
DIE("problem writing file"); |
452 |
drms_free_array(outArray); |
453 |
} |
454 |
|
455 |
if (inseg >= 0) |
456 |
{ |
457 |
/* The caller requested the processing of a single segment. Exit loop. */ |
458 |
break; |
459 |
} |
460 |
|
461 |
/* If the user did not provide a segment specifier (which means that all segment structs are in the rec->segments container, |
462 |
* and there was no seglist), then process only the first segment. */ |
463 |
if (inRec->seriesinfo && *inRec->seriesinfo->seriesname) |
464 |
{ |
465 |
template = drms_template_record(drms_env, inRec->seriesinfo->seriesname, &status); |
466 |
} |
467 |
else |
468 |
{ |
469 |
template = NULL; |
470 |
} |
471 |
|
472 |
if (status || !template) |
473 |
{ |
474 |
DIE("Cannot obtain DRMS-record template."); |
475 |
} |
476 |
|
477 |
if (drms_record_numsegments(inRec) == drms_record_numsegments(template) && !hasSeglist) |
478 |
{ |
479 |
break; |
480 |
} |
481 |
} // end of segment loop |
482 |
|
483 |
if (segIter) |
484 |
{ |
485 |
hiter_destroy(&segIter); |
486 |
} |
487 |
} // record quality check |
488 |
} |
489 |
} //end of "irec" loop |
490 |
|
491 |
drms_close_records(inRS, DRMS_FREE_RECORD); |
492 |
drms_close_records(outRS, DRMS_INSERT_RECORD); |
493 |
return (DRMS_SUCCESS); |
494 |
} // end of DoIt |
495 |
|
496 |
// ---------------------------------------------------------------------- |
497 |
|
498 |
/* center whith whole pixel shifts and rotate by 180 if needed */ |
499 |
/* Only apply center if it will not result in an image crop. I.e. not ever |
500 |
for AIA, and not for HMI or MDI or other if a shift of more than 20 arcsec |
501 |
is implied */ |
502 |
int upNcenter(DRMS_Array_t *arr, ObsInfo_t *ObsLoc) |
503 |
{ |
504 |
int nx, ny, ix, iy, i, j, xoff, yoff, max_off; |
505 |
double rot, x0, y0, midx, midy; |
506 |
float *data; |
507 |
float *data2; |
508 |
if (!arr || !ObsLoc) |
509 |
return(1); |
510 |
data = arr->data; |
511 |
nx = arr->axis[0]; |
512 |
ny = arr->axis[1]; |
513 |
x0 = ObsLoc->crpix1 - 1; |
514 |
y0 = ObsLoc->crpix2 - 1; |
515 |
midx = (nx-1.0)/2.0; |
516 |
midy = (ny-1.0)/2.0; |
517 |
if ((rot = fabs(ObsLoc->crota2)) > 179 && rot < 181) |
518 |
{ |
519 |
// rotate image by 180 degrees by a flip flip |
520 |
float val; |
521 |
int half = ny / 2; |
522 |
int odd = ny & 1; |
523 |
for (iy=0; iy<half; iy++) |
524 |
{ |
525 |
for (ix=0; ix<nx; ix++) |
526 |
{ |
527 |
i = iy*nx + ix; |
528 |
j = (ny - 1 - iy)*nx + (nx - 1 - ix); |
529 |
val = data[i]; |
530 |
data[i] = data[j]; |
531 |
data[j] = val; |
532 |
} |
533 |
} |
534 |
// reverse middle row if ny is odd |
535 |
if (odd) { |
536 |
for (ix=0; ix<nx/2; ++ix) { |
537 |
i = nx*half + ix; |
538 |
j = nx*half + nx - ix - 1; |
539 |
val = data[i]; |
540 |
data[i] = data[j]; |
541 |
data[j] = val; |
542 |
} |
543 |
} |
544 |
x0 = nx - 1 - x0; |
545 |
y0 = ny - 1 - y0; |
546 |
rot = ObsLoc->crota2 - 180.0; |
547 |
if (rot < -90.0) rot += 360.0; |
548 |
ObsLoc->crota2 = rot; |
549 |
} |
550 |
// Center to nearest pixel - if OK to do so |
551 |
xoff = round(x0 - midx); |
552 |
yoff = round(y0 - midy); |
553 |
max_off = 20.0 / ObsLoc->cdelt1; |
554 |
if (arr->parent_segment && |
555 |
arr->parent_segment->record && |
556 |
arr->parent_segment->record->seriesinfo && |
557 |
arr->parent_segment->record->seriesinfo->seriesname && |
558 |
strncasecmp(arr->parent_segment->record->seriesinfo->seriesname, "aia", 3) && |
559 |
abs(xoff) < max_off && abs(yoff) < max_off) |
560 |
{ |
561 |
if (abs(xoff) >= 1 || abs(yoff) >= 1) |
562 |
{ |
563 |
data2 = malloc(4*nx*ny); |
564 |
for (iy=0; iy<ny; iy++) |
565 |
{ |
566 |
int jy = iy + yoff; |
567 |
for (ix=0; ix<nx; ix++) |
568 |
{ |
569 |
int jx = ix + xoff; |
570 |
int idx = jy*nx + jx; |
571 |
int idx2 = iy*nx + ix; |
572 |
if (jx<0 || jx>=nx || jy<0 || jy>=ny) |
573 |
data2[idx2] = DRMS_MISSING_FLOAT; |
574 |
else |
575 |
data2[idx2] = data[idx]; |
576 |
} |
577 |
} |
578 |
x0 -= xoff; |
579 |
y0 -= yoff; |
580 |
free(data); |
581 |
arr->data = data2; |
582 |
} |
583 |
} |
584 |
// update center location |
585 |
ObsLoc->crpix1 = x0 + 1; |
586 |
ObsLoc->crpix2 = y0 + 1; |
587 |
return(0); |
588 |
} |
589 |
|
590 |
// ---------------------------------------------------------------------- |
591 |
|
592 |
int crop_image(DRMS_Array_t *arr, ObsInfo_t *ObsLoc) |
593 |
{ |
594 |
int nx, ny, ix, iy, i, j, xoff, yoff; |
595 |
double x0, y0; |
596 |
double rsun = ObsLoc->rsun_obs/ObsLoc->cdelt1; |
597 |
double scale, crop_limit2; |
598 |
float *data; |
599 |
if (!arr || !ObsLoc) |
600 |
return(1); |
601 |
data = arr->data; |
602 |
nx = arr->axis[0]; |
603 |
ny = arr->axis[1]; |
604 |
x0 = ObsLoc->crpix1 - 1; |
605 |
y0 = ObsLoc->crpix2 - 1; |
606 |
scale = 1.0/rsun; |
607 |
// crop_limit = 0.99975; // 1 - 1/4000, 1/2 HMI pixel. |
608 |
crop_limit2 = 0.99950; // square of 1 - 1/4000, 1/2 HMI pixel. |
609 |
for (iy=0; iy<ny; iy++) |
610 |
for (ix=0; ix<nx; ix++) |
611 |
{ |
612 |
double x, y, R2; |
613 |
float *Ip = data + iy*nx + ix; |
614 |
if (drms_ismissing_float(*Ip)) |
615 |
continue; |
616 |
x = ((double)ix - x0) * scale; /* x,y in pixel coords */ |
617 |
y = ((double)iy - y0) * scale; |
618 |
R2 = x*x + y*y; |
619 |
if (R2 > crop_limit2) |
620 |
*Ip = DRMS_MISSING_FLOAT; |
621 |
} |
622 |
return(0); |
623 |
} |
624 |
|
625 |
// ---------------------------------------------------------------------- |
626 |
|
627 |
#define CHECK(keyname) {if (status) {fprintf(stderr,"Keyword failure to find: %s, status=%d\n",keyname,status); *rstatus=status; return(NULL);}} |
628 |
|
629 |
ObsInfo_t *GetObsInfo(DRMS_Segment_t *seg, ObsInfo_t *pObsLoc, int *rstatus) |
630 |
{ |
631 |
TIME t_prev; |
632 |
DRMS_Record_t *rec; |
633 |
TIME t_obs; |
634 |
double dv; |
635 |
ObsInfo_t *ObsLoc; |
636 |
int status; |
637 |
|
638 |
if (!seg || !(rec = seg->record)) |
639 |
{ *rstatus = 1; return(NULL); } |
640 |
|
641 |
ObsLoc = (pObsLoc ? pObsLoc : (ObsInfo_t *)malloc(sizeof(ObsInfo_t))); |
642 |
if (!pObsLoc) |
643 |
memset(ObsLoc, 0, sizeof(ObsInfo_t)); |
644 |
|
645 |
t_prev = ObsLoc->t_obs; |
646 |
t_obs = drms_getkey_time(rec, "T_OBS", &status); CHECK("T_OBS"); |
647 |
|
648 |
if (t_obs <= 0.0) |
649 |
{ *rstatus = 2; return(NULL); } |
650 |
|
651 |
if (t_obs != t_prev) |
652 |
{ |
653 |
ObsLoc->crpix1 = drms_getkey_double(rec, "CRPIX1", &status); CHECK("CRPIX1"); |
654 |
ObsLoc->crpix2 = drms_getkey_double(rec, "CRPIX2", &status); CHECK("CRPIX2"); |
655 |
ObsLoc->crval1 = drms_getkey_double(rec, "CRVAL1", &status); CHECK("CRVAL1"); |
656 |
ObsLoc->crval2 = drms_getkey_double(rec, "CRVAL2", &status); CHECK("CRVAL2"); |
657 |
ObsLoc->cdelt1 = drms_getkey_double(rec, "CDELT1", &status); CHECK("CDELT1"); |
658 |
ObsLoc->cdelt2 = drms_getkey_double(rec, "CDELT2", &status); CHECK("CDELT1"); |
659 |
ObsLoc->crota2 = drms_getkey_double(rec, "CROTA2", &status); if (status) ObsLoc->crota2 = 0.0; // WCS default |
660 |
ObsLoc->sina = sin(ObsLoc->crota2*Deg2Rad); |
661 |
ObsLoc->cosa = sqrt (1.0 - ObsLoc->sina*ObsLoc->sina); |
662 |
ObsLoc->rsun_obs = drms_getkey_double(rec, "RSUN_OBS", &status); |
663 |
if (status) |
664 |
{ |
665 |
double dsun_obs = drms_getkey_double(rec, "DSUN_OBS", &status); CHECK("DSUN_OBS"); |
666 |
ObsLoc->rsun_obs = asin(696000000.0/dsun_obs)/arcsec2Rad; |
667 |
} |
668 |
ObsLoc->obs_vr = drms_getkey_double(rec, "OBS_VR", &status); CHECK("OBS_VR"); |
669 |
ObsLoc->obs_vw = drms_getkey_double(rec, "OBS_VW", &status); CHECK("OBS_VW"); |
670 |
ObsLoc->obs_vn = drms_getkey_double(rec, "OBS_VN", &status); CHECK("OBS_VN"); |
671 |
ObsLoc->obs_b0 = drms_getkey_double(rec, "CRLT_OBS", &status); CHECK("CRLT_OBS"); |
672 |
ObsLoc->t_obs = t_obs; |
673 |
} |
674 |
*rstatus = 0; |
675 |
return(ObsLoc); |
676 |
} |
677 |
|
678 |
/* GetMinObsInfo - gets minimum standard WCS keywords for e.g. heliographic mapped data */ |
679 |
ObsInfo_t *GetMinObsInfo(DRMS_Segment_t *seg, ObsInfo_t *pObsLoc, int *rstatus) |
680 |
{ |
681 |
TIME t_prev; |
682 |
DRMS_Record_t *rec; |
683 |
TIME t_obs; |
684 |
double dv; |
685 |
ObsInfo_t *ObsLoc; |
686 |
int status; |
687 |
|
688 |
if (!seg || !(rec = seg->record)) |
689 |
{ *rstatus = 1; return(NULL); } |
690 |
|
691 |
ObsLoc = (pObsLoc ? pObsLoc : (ObsInfo_t *)malloc(sizeof(ObsInfo_t))); |
692 |
if (!pObsLoc) |
693 |
memset(ObsLoc, 0, sizeof(ObsInfo_t)); |
694 |
|
695 |
t_prev = ObsLoc->t_obs; |
696 |
t_obs = drms_getkey_time(rec, "T_OBS", &status); CHECK("T_OBS"); |
697 |
|
698 |
if (t_obs <= 0.0) |
699 |
{ *rstatus = 2; return(NULL); } |
700 |
|
701 |
if (t_obs != t_prev) |
702 |
{ |
703 |
ObsLoc->crpix1 = drms_getkey_double(rec, "CRPIX1", &status); CHECK("CRPIX1"); |
704 |
ObsLoc->crpix2 = drms_getkey_double(rec, "CRPIX2", &status); CHECK("CRPIX2"); |
705 |
ObsLoc->crval1 = drms_getkey_double(rec, "CRVAL1", &status); CHECK("CRVAL1"); |
706 |
ObsLoc->crval2 = drms_getkey_double(rec, "CRVAL2", &status); CHECK("CRVAL2"); |
707 |
ObsLoc->cdelt1 = drms_getkey_double(rec, "CDELT1", &status); CHECK("CDELT1"); |
708 |
ObsLoc->cdelt2 = drms_getkey_double(rec, "CDELT2", &status); CHECK("CDELT1"); |
709 |
ObsLoc->crota2 = drms_getkey_double(rec, "CROTA2", &status); if (status) ObsLoc->crota2 = 0.0; // WCS default |
710 |
ObsLoc->sina = sin(ObsLoc->crota2*Deg2Rad); |
711 |
ObsLoc->cosa = sqrt (1.0 - ObsLoc->sina*ObsLoc->sina); |
712 |
ObsLoc->rsun_obs = drms_getkey_double(rec, "RSUN_OBS", &status); |
713 |
ObsLoc->obs_vr = drms_getkey_double(rec, "OBS_VR", &status); |
714 |
ObsLoc->obs_vw = drms_getkey_double(rec, "OBS_VW", &status); |
715 |
ObsLoc->obs_vn = drms_getkey_double(rec, "OBS_VN", &status); |
716 |
ObsLoc->obs_b0 = drms_getkey_double(rec, "CRLT_OBS", &status); |
717 |
ObsLoc->t_obs = t_obs; |
718 |
} |
719 |
*rstatus = 0; |
720 |
return(ObsLoc); |
721 |
} |
722 |
|
723 |
// ---------------------------------------------------------------------- |
724 |
|
725 |
// In cases known to not have compact slotted series and cadence is specified |
726 |
// generate explicit recordset list of closest good record to desired grid |
727 |
// First get vector of times and quality |
728 |
// Then if vector is not OK, quit. |
729 |
// then: make temp file to hold recordset list |
730 |
// start with first time to define desired grid, |
731 |
// make array of desired times. |
732 |
// make empty array of recnums |
733 |
// search vector for good images nearest desired times |
734 |
// for each found time, write record query |
735 |
|
736 |
|
737 |
const char *get_input_recset(DRMS_Env_t *drms_env, const char *inQuery) |
738 |
{ |
739 |
static char newInQuery[102]; |
740 |
TIME epoch = (cmdparams_exists(&cmdparams, "epoch")) ? params_get_time(&cmdparams, "epoch") : 0; |
741 |
DRMS_Array_t *data; |
742 |
TIME t_start, t_stop, t_now, t_want, t_diff, this_t_diff; |
743 |
int status = 1; |
744 |
int nrecs, irec; |
745 |
int nslots, islot; |
746 |
long long *recnums; |
747 |
TIME *t_this, half; |
748 |
TIME cadence; |
749 |
double *drecnum, *dquality; |
750 |
int quality; |
751 |
long long recnum; |
752 |
char keylist[DRMS_MAXQUERYLEN]; |
753 |
static char filename[100]; |
754 |
char *tmpdir; |
755 |
FILE *tmpfile; |
756 |
char newIn[DRMS_MAXQUERYLEN]; |
757 |
char seriesname[DRMS_MAXQUERYLEN]; |
758 |
char *lbracket; |
759 |
char *at = index(inQuery, '@'); |
760 |
|
761 |
if (at && *at && (strncmp(inQuery,"aia.lev1[", 9)==0 || |
762 |
strncmp(inQuery,"hmi.lev1[", 9)==0 || |
763 |
strncmp(inQuery,"aia.lev1_nrt2[",14)==0 || |
764 |
strncmp(inQuery,"hmi.lev1_nrt[", 13)==0 )) |
765 |
{ |
766 |
char *ip=(char *)inQuery, *op=newIn, *p; |
767 |
long n, mul; |
768 |
|
769 |
char *segSpec = NULL; |
770 |
const char *psl = NULL; |
771 |
char *pbracket = NULL; |
772 |
|
773 |
psl = strchr(inQuery, '{'); |
774 |
if (psl) |
775 |
{ |
776 |
segSpec = strdup(psl + 1); |
777 |
|
778 |
if (!segSpec) |
779 |
{ |
780 |
fprintf(stderr, "No memory.\n"); |
781 |
return NULL; |
782 |
} |
783 |
|
784 |
pbracket = strchr(segSpec, '}'); |
785 |
|
786 |
if (!pbracket) |
787 |
{ |
788 |
fprintf(stderr, "Invalid segment specification.\n"); |
789 |
return NULL; |
790 |
} |
791 |
|
792 |
*pbracket = '\0'; |
793 |
|
794 |
if (!*segSpec) |
795 |
{ |
796 |
fprintf(stderr, "Invalid segment specification.\n"); |
797 |
return NULL; |
798 |
} |
799 |
} |
800 |
|
801 |
while ( *ip && ip<at ) |
802 |
*op++ = *ip++; |
803 |
ip++; // skip the '@' |
804 |
n = strtol(ip, &p, 10); // get digits only |
805 |
if (*p == 's') mul = 1; |
806 |
else if (*p == 'm') mul = 60; |
807 |
else if (*p == 'h') mul = 3600; |
808 |
else if (*p == 'd') mul = 86400; |
809 |
else |
810 |
{ |
811 |
fprintf(stderr,"cant make sense of @xx cadence spec for aia or hmi lev1 data"); |
812 |
return(NULL); |
813 |
} |
814 |
cadence = n * mul; |
815 |
ip = ++p; // skip cadence multiplier |
816 |
while ( *ip ) |
817 |
*op++ = *ip++; |
818 |
*op = '\0'; |
819 |
half = cadence/2.0; |
820 |
sprintf(keylist, "T_OBS,QUALITY,recnum"); |
821 |
data = drms_record_getvector(drms_env, newIn, keylist, DRMS_TYPE_DOUBLE, 0, &status); |
822 |
if (!data || status) |
823 |
{ |
824 |
fprintf(stderr,"getkey_vector failed status=%d\n", status); |
825 |
return(NULL); |
826 |
} |
827 |
nrecs = data->axis[1]; |
828 |
irec = 0; |
829 |
t_this = (TIME *)data->data; |
830 |
dquality = (double *)data->data + 1*nrecs; |
831 |
drecnum = (double *)data->data + 2*nrecs; |
832 |
if (epoch > 0.0) |
833 |
{ |
834 |
int s0 = (t_this[0] - epoch)/cadence; |
835 |
TIME t0 = s0*cadence + epoch; |
836 |
t_start = (t0 < t_this[0] ? t0 + cadence : t0); |
837 |
} |
838 |
else |
839 |
t_start = t_this[0]; |
840 |
t_stop = t_this[nrecs-1]; |
841 |
nslots = (t_stop - t_start + cadence/2)/cadence; |
842 |
recnums = (long long *)malloc(nslots*sizeof(long long)); |
843 |
for (islot=0; islot<nslots; islot++) |
844 |
recnums[islot] = 0; |
845 |
islot = 0; |
846 |
t_want = t_start; |
847 |
t_diff = 1.0e9; |
848 |
for (irec = 0; irec<nrecs; irec++) |
849 |
{ |
850 |
t_now = t_this[irec]; |
851 |
quality = (int)dquality[irec] & 0xFFFFFFFF; |
852 |
recnum = (long long)drecnum[irec]; |
853 |
this_t_diff = fabs(t_now - t_want); |
854 |
if (quality < 0) |
855 |
continue; |
856 |
if (t_now <= (t_want-half)) |
857 |
continue; |
858 |
while (t_now > (t_want+half)) |
859 |
{ |
860 |
islot++; |
861 |
if (islot >= nslots) |
862 |
break; |
863 |
t_want = t_start + cadence * islot; |
864 |
this_t_diff = fabs(t_now - t_want); |
865 |
t_diff = 1.0e8; |
866 |
} |
867 |
if (islot < nslots && this_t_diff <= t_diff) |
868 |
recnums[islot] = recnum; |
869 |
t_diff = fabs(t_now - t_want); |
870 |
} |
871 |
if (islot+1 < nslots) |
872 |
nslots = islot+1; // take what we got. |
873 |
strcpy(seriesname, inQuery); |
874 |
lbracket = index(seriesname,'['); |
875 |
if (lbracket) *lbracket = '\0'; |
876 |
|
877 |
tmpdir = getenv("TMPDIR"); |
878 |
if (!tmpdir) tmpdir = "/tmp"; |
879 |
sprintf(filename, "%s/hg_patchXXXXXX", tmpdir); |
880 |
mkstemp(filename); |
881 |
tmpfile = fopen(filename,"w"); |
882 |
for (islot=0; islot<nslots; islot++) |
883 |
if (recnums[islot]) |
884 |
{ |
885 |
if (!segSpec) |
886 |
{ |
887 |
fprintf(tmpfile, "%s[:#%lld]\n", seriesname, recnums[islot]); |
888 |
} |
889 |
else |
890 |
{ |
891 |
fprintf(tmpfile, "%s[:#%lld]{%s}\n", seriesname, recnums[islot], segSpec); |
892 |
} |
893 |
} |
894 |
|
895 |
if (segSpec) |
896 |
{ |
897 |
free(segSpec); |
898 |
} |
899 |
|
900 |
fclose(tmpfile); |
901 |
free(recnums); |
902 |
drms_free_array(data); |
903 |
sprintf(newInQuery,"@%s", filename); |
904 |
return(newInQuery); |
905 |
} |
906 |
else |
907 |
return(inQuery); |
908 |
} |