/* * (C) Copyright 2005- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction. */ #include "grib_api_internal.h" #include /* This is used by make_class.pl START_CLASS_DEF CLASS = iterator SUPER = grib_iterator_class_gen IMPLEMENTS = destroy IMPLEMENTS = init;next MEMBERS = double *lats MEMBERS = double *lons MEMBERS = long Nj END_CLASS_DEF */ /* START_CLASS_IMP */ /* Don't edit anything between START_CLASS_IMP and END_CLASS_IMP Instead edit values between START_CLASS_DEF and END_CLASS_DEF or edit "iterator.class" and rerun ./make_class.pl */ static void init_class(grib_iterator_class*); static int init(grib_iterator* i, grib_handle*, grib_arguments*); static int next(grib_iterator* i, double* lat, double* lon, double* val); static int destroy(grib_iterator* i); typedef struct grib_iterator_polar_stereographic { grib_iterator it; /* Members defined in gen */ long carg; const char* missingValue; /* Members defined in polar_stereographic */ double* lats; double* lons; long Nj; } grib_iterator_polar_stereographic; extern grib_iterator_class* grib_iterator_class_gen; static grib_iterator_class _grib_iterator_class_polar_stereographic = { &grib_iterator_class_gen, /* super */ "polar_stereographic", /* name */ sizeof(grib_iterator_polar_stereographic), /* size of instance */ 0, /* inited */ &init_class, /* init_class */ &init, /* constructor */ &destroy, /* destructor */ &next, /* Next Value */ 0, /* Previous Value */ 0, /* Reset the counter */ 0, /* has next values */ }; grib_iterator_class* grib_iterator_class_polar_stereographic = &_grib_iterator_class_polar_stereographic; static void init_class(grib_iterator_class* c) { c->previous = (*(c->super))->previous; c->reset = (*(c->super))->reset; c->has_next = (*(c->super))->has_next; } /* END_CLASS_IMP */ static int next(grib_iterator* i, double* lat, double* lon, double* val) { grib_iterator_polar_stereographic* self = (grib_iterator_polar_stereographic*)i; if ((long)i->e >= (long)(i->nv - 1)) return 0; i->e++; *lat = self->lats[i->e]; *lon = self->lons[i->e]; *val = i->data[i->e]; return 1; } /* Data struct for Forward and Inverse Projections */ typedef struct proj_data_t { double centre_lon; /* central longitude */ double centre_lat; /* central latitude */ double sign; /* sign variable */ double ind; /* flag variable */ double mcs; /* small m */ double tcs; /* small t */ double false_northing; /* y offset in meters */ double false_easting; /* x offset in meters */ } proj_data_t; #define RAD2DEG 57.29577951308232087684 /* 180 over pi */ #define DEG2RAD 0.01745329251994329576 /* pi over 180 */ #define PI_OVER_2 1.5707963267948966 /* half pi */ #define EPSILON 1.0e-10 static int init(grib_iterator* iter, grib_handle* h, grib_arguments* args) { int ret = 0; double *lats, *lons; /* arrays for latitudes and longitudes */ double lonFirstInDegrees, latFirstInDegrees, radius; double x, y, Dx, Dy; long nx, ny, centralLongitudeInDegrees, centralLatitudeInDegrees; long alternativeRowScanning, iScansNegatively, i, j; long jScansPositively, jPointsAreConsecutive, southPoleOnPlane; double centralLongitude, centralLatitude; /* in radians */ double con1; /* temporary angle */ double ts; /* value of small t */ double height; /* height above ellipsoid */ double x0, y0, lonFirst, latFirst; proj_data_t fwd_proj_data = {0,}; proj_data_t inv_proj_data = {0,}; grib_iterator_polar_stereographic* self = (grib_iterator_polar_stereographic*)iter; const char* sradius = grib_arguments_get_name(h, args, self->carg++); const char* snx = grib_arguments_get_name(h, args, self->carg++); const char* sny = grib_arguments_get_name(h, args, self->carg++); const char* slatFirstInDegrees = grib_arguments_get_name(h, args, self->carg++); const char* slonFirstInDegrees = grib_arguments_get_name(h, args, self->carg++); const char* ssouthPoleOnPlane = grib_arguments_get_name(h, args, self->carg++); const char* scentralLongitude = grib_arguments_get_name(h, args, self->carg++); const char* scentralLatitude = grib_arguments_get_name(h, args, self->carg++); const char* sDx = grib_arguments_get_name(h, args, self->carg++); const char* sDy = grib_arguments_get_name(h, args, self->carg++); const char* siScansNegatively = grib_arguments_get_name(h, args, self->carg++); const char* sjScansPositively = grib_arguments_get_name(h, args, self->carg++); const char* sjPointsAreConsecutive = grib_arguments_get_name(h, args, self->carg++); const char* salternativeRowScanning = grib_arguments_get_name(h, args, self->carg++); if (grib_is_earth_oblate(h)) { grib_context_log(h->context, GRIB_LOG_ERROR, "Polar stereographic only supported for spherical earth."); return GRIB_GEOCALCULUS_PROBLEM; } if ((ret = grib_get_double_internal(h, sradius, &radius)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, snx, &nx)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, sny, &ny)) != GRIB_SUCCESS) return ret; if (iter->nv != nx * ny) { grib_context_log(h->context, GRIB_LOG_ERROR, "Wrong number of points (%ld!=%ldx%ld)", iter->nv, nx, ny); return GRIB_WRONG_GRID; } if ((ret = grib_get_double_internal(h, slatFirstInDegrees, &latFirstInDegrees)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_double_internal(h, slonFirstInDegrees, &lonFirstInDegrees)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, ssouthPoleOnPlane, &southPoleOnPlane)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, scentralLongitude, ¢ralLongitudeInDegrees)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, scentralLatitude, ¢ralLatitudeInDegrees)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_double_internal(h, sDx, &Dx)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_double_internal(h, sDy, &Dy)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, sjPointsAreConsecutive, &jPointsAreConsecutive)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, sjScansPositively, &jScansPositively)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, siScansNegatively, &iScansNegatively)) != GRIB_SUCCESS) return ret; if ((ret = grib_get_long_internal(h, salternativeRowScanning, &alternativeRowScanning)) != GRIB_SUCCESS) return ret; centralLongitude = centralLongitudeInDegrees * DEG2RAD; centralLatitude = centralLatitudeInDegrees * DEG2RAD; lonFirst = lonFirstInDegrees * DEG2RAD; latFirst = latFirstInDegrees * DEG2RAD; /* Forward projection initialisation */ fwd_proj_data.false_northing = 0; fwd_proj_data.false_easting = 0; fwd_proj_data.centre_lon = centralLongitude; fwd_proj_data.centre_lat = centralLatitude; if (centralLatitude < 0) fwd_proj_data.sign = -1.0; else fwd_proj_data.sign = +1.0; fwd_proj_data.ind = 0; if (fabs(fabs(centralLatitude) - PI_OVER_2) > EPSILON) { /* central latitude different from 90 i.e. not north/south polar */ fwd_proj_data.ind = 1; con1 = fwd_proj_data.sign * centralLatitude; fwd_proj_data.mcs = cos(con1); fwd_proj_data.tcs = tan(0.5 * (PI_OVER_2 - con1)); } /* Forward projection from initial lat,lon to initial x,y */ con1 = fwd_proj_data.sign * (lonFirst - fwd_proj_data.centre_lon); ts = tan(0.5 * (PI_OVER_2 - fwd_proj_data.sign * latFirst)); if (fwd_proj_data.ind) height = radius * fwd_proj_data.mcs * ts / fwd_proj_data.tcs; else height = 2.0 * radius * ts; x0 = fwd_proj_data.sign * height * sin(con1) + fwd_proj_data.false_easting; y0 = -fwd_proj_data.sign * height * cos(con1) + fwd_proj_data.false_northing; x0 = -x0; y0 = -y0; /* Inverse projection initialisation */ inv_proj_data.false_easting = x0; inv_proj_data.false_northing = y0; inv_proj_data.centre_lon = centralLongitude; inv_proj_data.centre_lat = centralLatitude; if (centralLatitude < 0) inv_proj_data.sign = -1.0; else inv_proj_data.sign = +1.0; inv_proj_data.ind = 0; if (fabs(fabs(centralLatitude) - PI_OVER_2) > EPSILON) { inv_proj_data.ind = 1; con1 = inv_proj_data.sign * inv_proj_data.centre_lat; inv_proj_data.mcs = cos(con1); inv_proj_data.tcs = tan(0.5 * (PI_OVER_2 - con1)); } self->lats = (double*)grib_context_malloc(h->context, iter->nv * sizeof(double)); if (!self->lats) { grib_context_log(h->context, GRIB_LOG_ERROR, "Error allocating %ld bytes", iter->nv * sizeof(double)); return GRIB_OUT_OF_MEMORY; } self->lons = (double*)grib_context_malloc(h->context, iter->nv * sizeof(double)); if (!self->lats) { grib_context_log(h->context, GRIB_LOG_ERROR, "Error allocating %ld bytes", iter->nv * sizeof(double)); return GRIB_OUT_OF_MEMORY; } lats = self->lats; lons = self->lons; Dx = iScansNegatively == 0 ? Dx : -Dx; Dy = jScansPositively == 1 ? Dy : -Dy; y = 0; for (j = 0; j < ny; j++) { x = 0; for (i = 0; i < nx; i++) { /* Inverse projection from x,y to lat,lon */ /* int index =i+j*nx; */ double _x = (x - inv_proj_data.false_easting) * inv_proj_data.sign; double _y = (y - inv_proj_data.false_northing) * inv_proj_data.sign; double rh = sqrt(_x * _x + _y * _y); if (inv_proj_data.ind) ts = rh * inv_proj_data.tcs / (radius * inv_proj_data.mcs); else ts = rh / (radius * 2.0); *lats = inv_proj_data.sign * (PI_OVER_2 - 2 * atan(ts)); if (rh == 0) { *lons = inv_proj_data.sign * inv_proj_data.centre_lon; } else { double temp = atan2(_x, -_y); *lons = inv_proj_data.sign * temp + inv_proj_data.centre_lon; } *lats = *lats * RAD2DEG; *lons = *lons * RAD2DEG; while (*lons < 0) *lons += 360; while (*lons > 360) *lons -= 360; lons++; lats++; x += Dx; } y += Dy; } #if 0 /*standardParallel = (southPoleOnPlane == 1) ? -90 : +90;*/ if (jPointsAreConsecutive) { x=xFirst; for (i=0;i360) *lons -= 360; lons++; lats++; y+=Dy; } x+=Dx; } } else { y=yFirst; for (j=0;j360) *lons -= 360; lons++; lats++; x+=Dx; } y+=Dy; } } #endif iter->e = -1; return ret; } static int destroy(grib_iterator* i) { grib_iterator_polar_stereographic* self = (grib_iterator_polar_stereographic*)i; const grib_context* c = i->h->context; grib_context_free(c, self->lats); grib_context_free(c, self->lons); return 1; }