27#include "pism/util/projection.hh"
28#include "pism/util/VariableMetadata.hh"
29#include "pism/util/error_handling.hh"
30#include "pism/util/io/File.hh"
31#include "pism/util/io/io_helpers.hh"
32#include "pism/util/Grid.hh"
33#include "pism/util/array/Scalar.hh"
34#include "pism/util/array/Array3D.hh"
36#include "pism/pism_config.hh"
37#include "pism/util/pism_utilities.hh"
40#include "pism/util/Proj.hh"
48 std::string::size_type position = std::string::npos;
49 for (
const auto &auth : {
"epsg:",
"EPSG:" }) {
50 position = proj_string.find(auth);
51 if (position != std::string::npos) {
56 if (position == std::string::npos) {
62 auto epsg_string = proj_string.substr(position + auth_len);
64 long int result = strtol(epsg_string.c_str(), &endptr, 10);
65 if (endptr == epsg_string.c_str()) {
73 e.
add_context(
"parsing the EPSG code in the PROJ string '%s'",
88 mapping[
"latitude_of_projection_origin"] = {90.0};
89 mapping[
"scale_factor_at_projection_origin"] = {1.0};
90 mapping[
"straight_vertical_longitude_from_pole"] = {-45.0};
91 mapping[
"standard_parallel"] = {70.0};
92 mapping[
"false_northing"] = {0.0};
93 mapping[
"grid_mapping_name"] =
"polar_stereographic";
94 mapping[
"false_easting"] = {0.0};
97 mapping[
"latitude_of_projection_origin"] = {-90.0};
98 mapping[
"scale_factor_at_projection_origin"] = {1.0};
99 mapping[
"straight_vertical_longitude_from_pole"] = {0.0};
100 mapping[
"standard_parallel"] = {-71.0};
101 mapping[
"false_northing"] = {0.0};
102 mapping[
"grid_mapping_name"] =
"polar_stereographic";
103 mapping[
"false_easting"] = {0.0};
106 mapping[
"grid_mapping_name"] =
"lambert_conformal_conic" ;
107 mapping[
"longitude_of_central_meridian"] = {-19.} ;
108 mapping[
"false_easting"] = {500000.} ;
109 mapping[
"false_northing"] = {500000.} ;
110 mapping[
"latitude_of_projection_origin"] = {65.} ;
111 mapping[
"standard_parallel"] = {64.25, 65.75} ;
112 mapping[
"long_name"] =
"CRS definition" ;
113 mapping[
"longitude_of_prime_meridian"] = {0.} ;
114 mapping[
"semi_major_axis"] = {6378137.} ;
115 mapping[
"inverse_flattening"] = {298.257222101} ;
118 mapping[
"latitude_of_projection_origin"] = {90.0};
119 mapping[
"scale_factor_at_projection_origin"] = {1.0};
120 mapping[
"straight_vertical_longitude_from_pole"] = {-150.0};
121 mapping[
"standard_parallel"] = {90.0};
122 mapping[
"false_northing"] = {2000000.0};
123 mapping[
"grid_mapping_name"] =
"polar_stereographic";
124 mapping[
"false_easting"] = {2000000.0};
127 mapping[
"longitude_of_central_meridian"] = {-123.0};
128 mapping[
"false_easting"] = {500000.0};
129 mapping[
"false_northing"] = {0.0};
130 mapping[
"grid_mapping_name"] =
"transverse_mercator";
131 mapping[
"inverse_flattening"] = {294.978698213898};
132 mapping[
"latitude_of_projection_origin"] = {0.0};
133 mapping[
"scale_factor_at_central_meridian"] = {0.9996};
134 mapping[
"semi_major_axis"] = {6378206.4};
135 mapping[
"unit"] =
"metre";
139 (
int)epsg, proj_string.c_str());
150 bool mapping_is_empty = not cf_mapping.
has_attribute(
"grid_mapping_name");
151 bool epsg_mapping_is_empty = not epsg_mapping.
has_attribute(
"grid_mapping_name");
153 if (mapping_is_empty and epsg_mapping_is_empty) {
164 "inconsistent metadata:\n"
165 "PROJ string \"%s\" requires %s = \"%s\",\n"
166 "but the mapping variable has no attribute \"%s\".",
167 proj_string.c_str(), s.first.c_str(), s.second.c_str(),
171 std::string
string = cf_mapping[s.first];
173 if (not(
string == s.second)) {
175 "inconsistent metadata:\n"
176 "%s requires %s = \"%s\",\n"
177 "but the mapping variable has %s = \"%s\".",
178 proj_string.c_str(), s.first.c_str(), s.second.c_str(),
179 s.first.c_str(),
string.c_str());
184 const double eps = 1e-12;
188 "inconsistent metadata:\n"
189 "%s requires %s = %f,\n"
190 "but the mapping variable has no attribute \"%s\".",
191 proj_string.c_str(), d.first.c_str(), d.second[0],
195 double value = cf_mapping.
get_number(d.first);
197 if (std::fabs(value - d.second[0]) > eps) {
199 "inconsistent metadata:\n"
200 "%s requires %s = %f,\n"
201 "but the mapping variable has %s = %f.",
202 proj_string.c_str(), d.first.c_str(), d.second[0],
203 d.first.c_str(), value);
211 std::vector<std::string> dimensions = file.
dimensions(variable_name);
213 if (dimensions.empty()) {
216 dimensions =
split(dimension_list,
' ');
219 if (dimensions.empty()) {
221 "variable '%s' in '%s' does not define a grid",
222 variable_name.c_str(), file.
name().c_str());
225 std::string result =
split(file.
name(),
'/').back();
227 for (
const auto &d : dimensions) {
236 if (piecewise_constant) {
237 result +=
":piecewise_constant";
247 for (
const auto &auth : {
"epsg:",
"EPSG:" }) {
248 if (
string.find(auth) != std::string::npos) {
271 std::string proj_string;
273 if (not mapping_variable_name.empty()) {
278 if (proj_string.empty()) {
284 if (proj_string.empty()) {
289 if (proj_string.empty()) {
311 auto mapping_variable_name = input_file.
read_text_attribute(variable_name,
"grid_mapping");
319 VariableMetadata cf_mapping(mapping_variable_name.empty() ?
"mapping" : mapping_variable_name,
331 if (cf_mapping.has_attribute(
"grid_mapping_name") and proj_is_epsg) {
337 e.
add_context(
"getting projection info from variable '%s' in '%s'", variable_name.c_str(),
338 input_file.
name().c_str());
344 if (cf_mapping.has_attribute(
"grid_mapping_name")) {
345 if (proj_string.empty()) {
355 cf_mapping =
epsg_to_cf(unit_system, proj_string);
361 if (not proj_string.empty()) {
362 cf_mapping[
"proj_params"] = proj_string;
370#if (Pism_USE_PROJ==1)
373static double triangle_area(
const double *A,
const double *B,
const double *C) {
375 for (
int j = 0; j < 3; ++j) {
381 return 0.5*sqrt(pow(V1[1]*V2[2] - V2[1]*V1[2], 2) +
382 pow(V1[0]*V2[2] - V2[0]*V1[2], 2) +
383 pow(V1[0]*V2[1] - V2[0]*V1[1], 2));
387 auto grid = result.grid();
389 Proj pism_to_geocent(projection,
"+proj=geocent +datum=WGS84 +ellps=WGS84");
402 double dx2 = 0.5 *
grid->dx(), dy2 = 0.5 *
grid->dy();
404 array::AccessScope list(result);
406 for (
auto p :
grid->points()) {
407 const int i = p.i(), j = p.j();
413 x_nw = x - dx2, y_nw = y + dy2,
414 x_ne = x + dx2, y_ne = y + dy2,
415 x_se = x + dx2, y_se = y - dy2,
416 x_sw = x - dx2, y_sw = y - dy2;
420 out = proj_trans(pism_to_geocent, PJ_FWD, proj_coord(x_nw, y_nw, 0, 0));
421 double nw[3] = {out.xyz.x, out.xyz.y, out.xyz.z};
423 out = proj_trans(pism_to_geocent, PJ_FWD, proj_coord(x_ne, y_ne, 0, 0));
424 double ne[3] = {out.xyz.x, out.xyz.y, out.xyz.z};
426 out = proj_trans(pism_to_geocent, PJ_FWD, proj_coord(x_se, y_se, 0, 0));
427 double se[3] = {out.xyz.x, out.xyz.y, out.xyz.z};
429 out = proj_trans(pism_to_geocent, PJ_FWD, proj_coord(x_sw, y_sw, 0, 0));
430 double sw[3] = {out.xyz.x, out.xyz.y, out.xyz.z};
437 LonLat which, array::Scalar &result) {
439 Proj crs(projection,
"EPSG:4326");
452 auto grid = result.grid();
454 array::AccessScope list{&result};
456 for (
auto p :
grid->points()) {
457 const int i = p.i(), j = p.j();
459 PJ_COORD out = proj_trans(crs, PJ_FWD, proj_coord(
grid->x(i),
grid->y(j), 0, 0));
463 result(i, j) = out.lp.lam;
466 result(i, j) = out.lp.phi;
473 array::Array3D &result) {
475 Proj crs(projection,
"EPSG:4326");
477 auto grid = result.grid();
479 double dx2 = 0.5 *
grid->dx(), dy2 = 0.5 *
grid->dy();
480 double x_offsets[] = {-dx2, dx2, dx2, -dx2};
481 double y_offsets[] = {-dy2, -dy2, dy2, dy2};
483 array::AccessScope list{&result};
485 for (
auto p :
grid->points()) {
486 const int i = p.i(), j = p.j();
488 double x0 =
grid->x(i), y0 =
grid->y(j);
490 double *values = result.get_column(i, j);
492 for (
int k = 0;
k < 4; ++
k) {
497 out = proj_trans(crs, PJ_FWD, proj_coord(x0 + x_offsets[
k], y0 + y_offsets[
k], 0, 0));
501 values[
k] = out.lp.lam;
504 values[
k] = out.lp.phi;
515 auto grid = result.
grid();
516 result.
set(grid->dx() * grid->dy());
526 " Please rebuild PISM with PROJ.");
537 " Please rebuild PISM with PROJ.");
563 double longitude_of_projection_origin = mapping[
"longitude_of_projection_origin"];
564 double latitude_of_projection_origin = mapping[
"latitude_of_projection_origin"];
565 double false_easting = mapping[
"false_easting"];
566 double false_northing = mapping[
"false_northing"];
568 std::vector<double> standard_parallel = mapping[
"standard_parallel"];
570 auto result =
pism::printf(
"+proj=aea +type=crs +lon_0=%f +lat_0=%f +x_0=%f +y_0=%f +lat_1=%f",
571 longitude_of_projection_origin,
572 latitude_of_projection_origin,
575 standard_parallel[0]);
576 if (standard_parallel.size() == 2) {
577 result +=
pism::printf(
" +lat_2=%f", standard_parallel[1]);
589 double longitude_of_projection_origin = mapping[
"longitude_of_projection_origin"];
590 double latitude_of_projection_origin = mapping[
"latitude_of_projection_origin"];
591 double false_easting = mapping[
"false_easting"];
592 double false_northing = mapping[
"false_northing"];
593 return pism::printf(
"+proj=aeqd +type=crs +lon_0=%f +lat_0=%f +x_0=%f +y_0=%f",
594 longitude_of_projection_origin,
595 latitude_of_projection_origin, false_easting, false_northing);
603 double longitude_of_projection_origin = mapping[
"longitude_of_projection_origin"];
604 double latitude_of_projection_origin = mapping[
"latitude_of_projection_origin"];
605 double false_easting = mapping[
"false_easting"];
606 double false_northing = mapping[
"false_northing"];
607 return pism::printf(
"+proj=laea +type=crs +lon_0=%f +lat_0=%f +x_0=%f +y_0=%f",
608 longitude_of_projection_origin,
609 latitude_of_projection_origin, false_easting, false_northing);
618 double longitude_of_projection_origin = mapping[
"longitude_of_projection_origin"];
619 double latitude_of_projection_origin = mapping[
"latitude_of_projection_origin"];
620 double false_easting = mapping[
"false_easting"];
621 double false_northing = mapping[
"false_northing"];
622 std::vector<double> standard_parallel = mapping[
"standard_parallel"];
624 auto result =
pism::printf(
"+proj=lcc +type=crs +lon_0=%f +lat_0=%f +x_0=%f +y_0=%f +lat_1=%f",
625 longitude_of_projection_origin,
626 latitude_of_projection_origin,
629 standard_parallel[0]);
630 if (standard_parallel.size() == 2) {
631 result +=
pism::printf(
" +lat_2=%f", standard_parallel[1]);
643 double longitude_of_central_meridian = mapping[
"longitude_of_central_meridian"];
644 double false_easting = mapping[
"false_easting"];
645 double false_northing = mapping[
"false_northing"];
647 auto result =
pism::printf(
"+proj=cea +type=crs +lon_0=%f +x_0=%f +y_0=%f",
648 longitude_of_central_meridian, false_easting, false_northing);
651 result +=
pism::printf(
" +lat_ts=%f", (
double)mapping[
"standard_parallel"]);
653 result +=
pism::printf(
" +k_0=%f", (
double)mapping[
"scale_factor_at_projection_origin"]);
661 return "+proj=lonlat +type=crs";
671 double longitude_of_projection_origin = mapping[
"longitude_of_projection_origin"];
672 double false_easting = mapping[
"false_easting"];
673 double false_northing = mapping[
"false_northing"];
675 auto result =
pism::printf(
"+proj=merc +type=crs +lon_0=%f +x_0=%f +y_0=%f",
676 longitude_of_projection_origin,
677 false_easting, false_northing);
680 result +=
pism::printf(
" +lat_ts=%f", (
double)mapping[
"standard_parallel"]);
682 result +=
pism::printf(
" +k_0=%f", (
double)mapping[
"scale_factor_at_projection_origin"]);
694 double longitude_of_projection_origin = mapping[
"longitude_of_projection_origin"];
695 double latitude_of_projection_origin = mapping[
"latitude_of_projection_origin"];
696 double false_easting = mapping[
"false_easting"];
697 double false_northing = mapping[
"false_northing"];
700 return pism::printf(
"+proj=ortho +type=crs +lon_0=%f +lat_0=%f +x_0=%f +y_0=%f",
701 longitude_of_projection_origin,
702 latitude_of_projection_origin,
717 double straight_vertical_longitude_from_pole = mapping[
"straight_vertical_longitude_from_pole"];
718 double latitude_of_projection_origin = mapping[
"latitude_of_projection_origin"];
719 double false_easting = mapping[
"false_easting"];
720 double false_northing = mapping[
"false_northing"];
722 auto result =
pism::printf(
"+proj=stere +type=crs +lat_0=%f +lon_0=%f +x_0=%f +y_0=%f",
723 latitude_of_projection_origin,
724 straight_vertical_longitude_from_pole,
729 result +=
pism::printf(
" +lat_ts=%f", (
double)mapping[
"standard_parallel"]);
731 result +=
pism::printf(
" +k_0=%f", (
double)mapping[
"scale_factor_at_projection_origin"]);
755 double grid_north_pole_latitude = mapping[
"grid_north_pole_latitude"];
756 double grid_north_pole_longitude = mapping[
"grid_north_pole_longitude"];
757 double north_pole_grid_longitude = 0.0;
760 north_pole_grid_longitude = mapping[
"north_pole_grid_longitude"];
763 return pism::printf(
"+proj=ob_tran +type=crs +o_proj=longlat +o_lon_p=%f +o_lat_p=%f +lon_0=%f",
764 north_pole_grid_longitude,
765 grid_north_pole_latitude,
766 180.0 + grid_north_pole_longitude);
778 double straight_vertical_longitude_from_pole = mapping[
"straight_vertical_longitude_from_pole"];
779 double latitude_of_projection_origin = mapping[
"latitude_of_projection_origin"];
780 double false_easting = mapping[
"false_easting"];
781 double false_northing = mapping[
"false_northing"];
782 double scale_factor_at_projection_origin = mapping[
"scale_factor_at_projection_origin"];
785 return pism::printf(
"+proj=stere +type=crs +lat_0=%f +lon_0=%f +x_0=%f +y_0=%f +k_0=%f",
786 latitude_of_projection_origin,
787 straight_vertical_longitude_from_pole,
790 scale_factor_at_projection_origin);
795 double scale_factor_at_central_meridian = mapping[
"scale_factor_at_central_meridian"];
796 double longitude_of_central_meridian = mapping[
"longitude_of_central_meridian"];
797 double latitude_of_projection_origin = mapping[
"latitude_of_projection_origin"];
798 double false_easting = mapping[
"false_easting"];
799 double false_northing = mapping[
"false_northing"];
801 return pism::printf(
"+proj=tmerc +type=crs +lon_0=%f +lat_0=%f +k=%f +x_0=%f +y_0=%f",
802 longitude_of_central_meridian,
803 latitude_of_projection_origin,
804 scale_factor_at_central_meridian,
816 std::string name = mapping[
"grid_mapping_name"];
824 auto flag = [&mapping](
const char* name,
const char* proj_name) {
826 return pism::printf(
" +%s=%f", proj_name, (
double)mapping[name]);
828 return std::string{};
834 result += flag(
"earth_radius",
"R");
835 result += flag(
"inverse_flattening",
"rf");
836 result += flag(
"semi_major_axis",
"a");
837 result += flag(
"semi_minor_axis",
"b");
840 result += flag(
"longitude_of_prime_meridian",
"pm");
859 std::string wkt = mapping[
"crs_wkt"];
860 if (not wkt.empty()) {
866 std::map<std::string, cf_to_proj_fn> mappings = {
882 std::string mapping_name = mapping[
"grid_mapping_name"];
884 if (mappings.find(mapping_name) == mappings.end()) {
886 "conversion CF -> PROJ for a '%s' grid is not implemented",
887 mapping_name.c_str());
890 return mappings[mapping_name](mapping) +
common_flags(mapping);
AxisType dimension_type(const std::string &name, units::System::Ptr unit_system) const
Get the "type" of a dimension.
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
std::vector< std::string > dimensions(const std::string &variable_name) const
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
High-level PISM I/O class.
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
A virtual class collecting methods common to ice and bedrock 3D fields.
std::shared_ptr< const Grid > grid() const
void set(double c)
Result: v[j] <- c for all j.
std::shared_ptr< System > Ptr
#define PISM_ERROR_LOCATION
VariableMetadata read_attributes(const File &file, const std::string &variable_name, std::shared_ptr< units::System > unit_system)
static std::string lambert_azimuthal_equal_area_to_proj(const VariableMetadata &mapping)
void check_consistency_epsg(const VariableMetadata &cf_mapping, const std::string &proj_string)
Check consistency of the "mapping" variable with the EPSG code in the proj string.
static bool contains_epsg(const std::string &string)
static std::string get_proj_parameters(const File &input_file, const std::string &mapping_variable_name)
static std::string mercator_to_proj(const VariableMetadata &mapping)
static std::string stereographic_to_proj(const VariableMetadata &mapping)
static std::string polar_stereographic_to_proj(const VariableMetadata &mapping)
static void compute_lon_lat_bounds(const std::string &projection, LonLat which, array::Array3D &result)
void compute_latitude(const std::string &projection, array::Scalar &result)
int parse_epsg(const std::string &proj_string)
static std::string lambert_conformal_conic_to_proj(const VariableMetadata &mapping)
std::string printf(const char *format,...)
void compute_longitude(const std::string &projection, array::Scalar &result)
static std::string azimuthal_equidistant_to_proj(const VariableMetadata &mapping)
VariableMetadata epsg_to_cf(units::System::Ptr system, const std::string &proj_string)
Return CF-Convention "mapping" variable corresponding to an EPSG code specified in a PROJ string.
VariableMetadata mapping_info_from_file(const File &input_file, const std::string &variable_name, units::System::Ptr unit_system)
std::string grid_name(const pism::File &file, const std::string &variable_name, pism::units::System::Ptr sys, bool piecewise_constant)
static std::string vertical_perspective_to_proj(const VariableMetadata &mapping)
static std::string transverse_mercator_to_proj(const VariableMetadata &mapping)
static double triangle_area(const Point &a, const Point &b, const Point &c)
static void compute_lon_lat(const std::string &projection, LonLat which, array::Scalar &result)
static std::string albers_conical_equal_area_to_proj(const VariableMetadata &mapping)
void compute_lat_bounds(const std::string &projection, array::Array3D &result)
static std::string rotated_latitude_longitude_to_proj(const VariableMetadata &mapping)
static std::string lambert_cylindrical_equal_area_to_proj(const VariableMetadata &mapping)
void compute_cell_areas(const std::string &projection, array::Scalar &result)
void compute_lon_bounds(const std::string &projection, array::Array3D &result)
std::string cf_to_proj(const VariableMetadata &mapping)
static std::string orthographic_to_proj(const VariableMetadata &mapping)
static std::string latitude_longitude_to_proj(const VariableMetadata &)
static std::string common_flags(const VariableMetadata &mapping)
std::vector< std::string > split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a vector of strings.
std::shared_ptr< Grid > grid(std::shared_ptr< Context > ctx)