cangyeone's picture
Publish audited synthetic reproducibility data and independent revision evidence
2b6ccf8 verified
Raw History Blame Contribute Delete
27.1 kB
/*--------------------------------------------------------------------
WARNING - this file is not gmt_map.c, it contains a subset of gmt_map.c
subset of functions selected by A. Lomax, June 1998
changes or additions indicated by */
/*AJL ... AJL*/
/*AJL*/ /*END AJL*/
/*--------------------------------------------------------------------*/
/*--------------------------------------------------------------------
* The GMT-system: @(#)gmt_map.c 2.56 09 Aug 1995
*
* Copyright (c) 1991-1995 by P. Wessel and W. H. F. Smith
* See README file for copying and redistribution conditions.
*--------------------------------------------------------------------*/
/*
*
* G M T _ M A P . C
*
*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* gmt_map.c contains code related to coordinate transformation
*
* Map Transformation Setup Routines
* These routines initializes the selected map transformation
* The names and main function are listed below
* NB! Note that the transformation function does not check that they are
* passed valid lon,lat numbers. I.e asking for log10 scaling using values
* <= 0 results in problems.
*
* Map_projections include functions that will set up the transformation
* between xy and latlon for several map projections.
*
* A few of the core coordinate transformation functions are based on similar
* FORTRAN routines written by Pat Manley, Doug Shearer, and Bill Haxby, and
* have been rewritten in C and subsequently streamlined. The rest is based
* on P. Snyder, "Map Projections - a working manual", USGS Prof paper 1395.
*
* Transformations supported (both forward and inverse):
* Linear x/y[/z] scaling
* Polar (theta, r) scaling
* Mercator
* Stereographic
* Albers Equal-Area Conic
* Lambert Conformal Conic
* Cassini Cylindrical
* Oblique Mercator
* TM Transverse Mercator
* UTM Universal Transverse Mercator
* Lambert Azimuthal Equal-Area
* Mollweide Equal-Area
* Hammer-Aitoff Equal-Area
* Sinusoidal Equal-Area
* Winkel Tripel
* Orthographic
* Azimuthal Equidistant
* Robinson
* Eckert IV
* Cylindrical Equal-area (e.g., Peters, Gall, Behrmann)
* Cylindrical Equidistant (Plate Carree)
*
* The ellipsoid used is selectable by editing the .gmtdefaults in your
* home directory. If no such file, create one by running gmtdefaults.
*
* Usage: Initialize system by calling map_setup (separate module), and
* then just use geo_to_xy() and xy_to_geo() functions.
*
* Author: Paul Wessel
* Date: 27-JUL-1992
* Version: v2.1
*
*
* Functions include:
*
...
*/
/*AJL*/
/*#include "gmt.h"*/
#include <string.h>
#include <math.h>
#include "map_project.h"
#define PI_2 (2.0*M_PI)
#define D2R (M_PI/180.0)
#define R2D (180.0/M_PI)
#define SMALL 1.0e-10
/*#define d_log(x) ((x) <= 0.0 ? gmt_NaN : log (x))*/
#define d_log(x) ((x) <= 0.0 ? -1.0e10 : log (x))
#define d_sqrt(x) ((x) < 0.0 ? 0.0 : sqrt (x))
/*double gmt_NaN;*/
typedef int BOOLEAN; /* BOOLEAN used for logical variables */
struct ELLIPSOID {
char name[20];
int date;
double eq_radius;
double pol_radius;
double flattening;
};
/* Information about a particular ellipsoid */
/* Table taken from Snyder "Map projection - a working manual",
p 12 Table 1 */
#define N_ELLIPSOIDS 15
struct ELLIPSOID ellipse[N_ELLIPSOIDS] = {
// name, date, eq_radius, pol_radius, flattening
{ "WGS-84", 1984, 6378137.0, 6356752.1, 1.0 / 298.254},
{ "GRS-80", 1980, 6378137.0, 6356752.3, 1.0 / 298.257},
{ "WGS-72", 1972, 6378135.0, 6356750.5, 1.0 / 298.26},
{ "Australian", 1965, 6378160.0, 6356774.7, 1.0 / 298.25},
{ "Krasovsky", 1940, 6378245.0, 6356863.0, 1.0 / 298.3},
{ "International", 1924, 6378388.0, 6356911.9, 1.0 / 297.0},
{ "Hayford-1909", 1909, 6378388.0, 6356911.9, 1.0 / 297.0},
{ "Clarke-1880", 1880, 6378249.1, 6356514.9, 1.0 / 293.46},
{ "Clarke-1866", 1866, 6378206.4, 6356583.8, 1.0 / 294.98},
{ "Airy", 1830, 6377563.4, 6356256.9, 1.0 / 299.32},
{ "Bessel", 1841, 6377397.2, 6356079.0, 1.0 / 299.15},
{ "Hayford-1830", 1830, 6377276.3, 6356075.4, 1.0 / 300.80},
{ "Sphere", 1980, 6371008.7714, 6371008.7714, 0.0},
/* https://gisgeography.com/geodetic-datums-nad27-nad83-wgs84/
NAD27 Datum vs NAD83 Datum
The NAD27 datum was based on the Clarke Ellipsoid of 1866:
Semi-major axis: 6,378,206.4 m
Semi-minor axis: 6,356,583.8 m
Inverse flattening: 294.98
The NAD83 datum was based on the Geodetic Reference System (GRS80) Ellipsoid:
Semi-major axis: 6,378,137.0 m
Semi-minor axis: 6,356,752.3 m
Inverse flattening: 298.26
*/
{ "NAD-27", 1927, 6378206.4, 6356583.8, 1.0 / 294.98},
{ "NAD-83", 1983, 6378137.4, 6356752.3, 1.0 / 294.26}
};
// number of projections supported
#define NUM_PROJ_MAX 10
double EQ_RAD[NUM_PROJ_MAX];
double ECC[NUM_PROJ_MAX], ECC2[NUM_PROJ_MAX], ECC4[NUM_PROJ_MAX], ECC6[NUM_PROJ_MAX];
//double M_PR_DEG;
/* fields from struct MAP_PROJECTIONS taken from gmt_project.h,
and converted to globals.
WARNING - many fields removed! */
BOOLEAN NorthPole[NUM_PROJ_MAX]; /* TRUE if projection is on northern
hermisphere, FALSE on southern */
double CentralMeridian[NUM_PROJ_MAX]; /* Central meridian for projection */
double Pole[NUM_PROJ_MAX]; /* +90 pr -90, depending on which pole */
/* Lambert conformal conic parameters.
(See Snyder for details on all parameters) */
double LambertConfConic_N[NUM_PROJ_MAX];
double LambertConfConic_F[NUM_PROJ_MAX];
double LambertConfConic_rho0[NUM_PROJ_MAX];
/* set constants that were set in GMT function map_setup */
/* use values from gmt_defaults.h: */
int map_setup_proxy(int n_proj, char* ellipsoid_name) {
int num_ellipsoid;
double f;
/*mknan (gmt_NaN);*/
/* determine ellipsoid */
for (num_ellipsoid = 0;
num_ellipsoid < N_ELLIPSOIDS
&& strcmp(ellipsoid_name, ellipse[num_ellipsoid].name);
num_ellipsoid++);
if (num_ellipsoid == N_ELLIPSOIDS)
return (-1);
EQ_RAD[n_proj] = ellipse[num_ellipsoid].eq_radius;
f = ellipse[num_ellipsoid].flattening;
ECC2[n_proj] = 2 * f - f * f;
ECC4[n_proj] = ECC2[n_proj] * ECC2[n_proj];
ECC6[n_proj] = ECC2[n_proj] * ECC4[n_proj];
ECC[n_proj] = d_sqrt(ECC2[n_proj]);
return (0);
}
/*END AJL*/
/*
* TRANSFORMATION ROUTINES FOR THE LAMBERT CONFORMAL CONIC PROJECTION
*/
/*** function to set up a Lambert Conformal Conic projection */
int vlamb(n_proj, rlong0, rlat0, pha, phb)
int n_proj;
double rlong0, rlat0, pha, phb;
{
double t_pha, m_pha, t_phb, m_phb, t_rlat0;
NorthPole[n_proj] = (rlat0 > 0.0);
// AJL 20090812 BUG FIX
Pole[n_proj] = (NorthPole[n_proj]) ? 90.0 : -90.0;
//Pole[n_proj] = (NorthPole) ? 90.0 : -90.0;
pha *= D2R;
phb *= D2R;
t_pha = tan(45.0 * D2R - 0.5 * pha) / pow((1.0 - ECC[n_proj] *
sin(pha)) / (1.0 + ECC[n_proj] * sin(pha)), 0.5 * ECC[n_proj]);
m_pha = cos(pha) / d_sqrt(1.0 - ECC2[n_proj]
* pow(sin(pha), 2.0));
t_phb = tan(45.0 * D2R - 0.5 * phb) / pow((1.0 - ECC[n_proj] *
sin(phb)) / (1.0 + ECC[n_proj] * sin(phb)), 0.5 * ECC[n_proj]);
m_phb = cos(phb) / d_sqrt(1.0 - ECC2[n_proj]
* pow(sin(phb), 2.0));
t_rlat0 = tan(45.0 * D2R - 0.5 * rlat0 * D2R) /
pow((1.0 - ECC[n_proj] * sin(rlat0 * D2R)) /
(1.0 + ECC[n_proj] * sin(rlat0 * D2R)), 0.5 * ECC[n_proj]);
if (pha != phb)
LambertConfConic_N[n_proj] = (d_log(m_pha) - d_log(m_phb))
/ (d_log(t_pha) - d_log(t_phb));
else
LambertConfConic_N[n_proj] = sin(pha);
LambertConfConic_F[n_proj] = m_pha / (LambertConfConic_N[n_proj] *
pow(t_pha, LambertConfConic_N[n_proj]));
CentralMeridian[n_proj] = rlong0;
LambertConfConic_rho0[n_proj] = EQ_RAD[n_proj] * LambertConfConic_F[n_proj] *
pow(t_rlat0, LambertConfConic_N[n_proj]);
return (0);
}
/*** function to do x,y to lat,long Lambert Conformal Conic projection */
int lamb(n_proj, lon, lat, x, y)
int n_proj;
double lon, lat, *x, *y;
{
double rho, theta, hold1, hold2, hold3;
while ((lon - CentralMeridian[n_proj]) < -180.0) lon += 360.0;
while ((lon - CentralMeridian[n_proj]) > 180.0) lon -= 360.0;
lat *= D2R;
hold2 = pow(((1.0 - ECC[n_proj] * sin(lat))
/ (1.0 + ECC[n_proj] * sin(lat))), 0.5 * ECC[n_proj]);
hold3 = tan(45.0 * D2R - 0.5 * lat);
if (fabs(hold3) < SMALL) hold3 = 0.0;
hold1 = (hold3 == 0.0) ? 0.0 : pow(hold3 / hold2, LambertConfConic_N[n_proj]);
rho = EQ_RAD[n_proj] * LambertConfConic_F[n_proj] * hold1;
theta = LambertConfConic_N[n_proj] * (lon - CentralMeridian[n_proj]) * D2R;
*x = rho * sin(theta);
*y = LambertConfConic_rho0[n_proj] - rho * cos(theta);
return (0);
}
/*** function to do lat,long to x,y inverse
Lambert Conformal Conic projection */
int ilamb(n_proj, lon, lat, x, y)
int n_proj;
double *lon, *lat, x, y;
{
int i;
double theta, temp, rho, t, tphi, phi, delta;
theta = atan(x / (LambertConfConic_rho0[n_proj] - y));
*lon = (theta / LambertConfConic_N[n_proj]) * R2D + CentralMeridian[n_proj];
temp = x * x + (LambertConfConic_rho0[n_proj] - y)
* (LambertConfConic_rho0[n_proj] - y);
rho = copysign(d_sqrt(temp), LambertConfConic_N[n_proj]);
t = pow((rho / (EQ_RAD[n_proj] * LambertConfConic_F[n_proj])), 1. / LambertConfConic_N[n_proj]);
tphi = 90.0 * D2R - 2.0 * atan(t);
delta = 1.0;
for (i = 0; i < 100 && delta > 1.0e-8; i++) {
temp = (1.0 - ECC[n_proj] * sin(tphi)) / (1.0 + ECC[n_proj] * sin(tphi));
phi = 90.0 * D2R - 2.0 * atan(t * pow(temp, 0.5 * ECC[n_proj]));
delta = fabs(fabs(tphi) - fabs(phi));
tphi = phi;
}
*lat = phi * R2D;
return (0);
}
/* Transverse Mercator Projection (TM) */
struct TRANS_MERCATOR {
BOOLEAN north_pole; /* TRUE if projection is on northern hemisphere, FALSE on southern */
int use_false_easting; // flag to apply false easting
long false_easting; // false easting to use (e.g. many UTM systems use 500km added to X when converting geog->UTM, and v.v.)
double map_scale_factor; // map scale factor
double central_meridian; // Central meridian for projection
double y_central_parallel; // y offset of central parallel
double t_e2;
double t_c1, t_c2, t_c3, t_c4;
double t_ic1, t_ic2, t_ic3, t_ic4;
};
struct TRANS_MERCATOR TransverseMercator[NUM_PROJ_MAX];
/*
* TRANSFORMATION ROUTINES FOR THE Transverse Mercator Projection (TM)
*/
/*
int map_init_tm(int n_proj) {
BOOLEAN search;
double xmin, xmax, ymin, ymax;
vtm(TransverseMercator[n_proj].pars[0]);
if (TransverseMercator[n_proj].units_pr_degree) TransverseMercator[n_proj].pars[1] /= M_PR_DEG;
TransverseMercator[n_proj].x_scale = TransverseMercator[n_proj].y_scale = TransverseMercator[n_proj].pars[1];
forward = (PFI) tm;
inverse = (PFI) itm;
if (TransverseMercator[n_proj].region) {
xy_search(&xmin, &xmax, &ymin, &ymax);
outside = (PFI) wesn_outside;
crossing = (PFI) wesn_crossing;
overlap = (PFI) wesn_overlap;
map_clip = (PFI) wesn_clip;
left_edge = (PFD) left_rect;
right_edge = (PFD) right_rect;
search = FALSE;
} else { // Find min values
(*forward) (TransverseMercator[n_proj].w, TransverseMercator[n_proj].s, &xmin, &ymin);
(*forward) (TransverseMercator[n_proj].e, TransverseMercator[n_proj].n, &xmax, &ymax);
outside = (PFI) rect_outside;
crossing = (PFI) rect_crossing;
overlap = (PFI) rect_overlap;
map_clip = (PFI) rect_clip;
left_edge = (PFD) left_rect;
right_edge = (PFD) right_rect;
frame_info.check_side = TRUE;
search = TRUE;
}
frame_info.horizontal = TRUE;
map_setinfo(xmin, xmax, ymin, ymax, TransverseMercator[n_proj].pars[1]);
gmtdefs.basemap_type = 1;
return (search);
}*/
void vtm(int n_proj, double lon0, double lat0, int use_false_easting, long false_easting, double map_scale_factor) {
/* Set up an TM projection */
double e1;
e1 = (1.0 - d_sqrt(1.0 - ECC2[n_proj])) / (1.0 + d_sqrt(1.0 - ECC2[n_proj]));
TransverseMercator[n_proj].t_e2 = ECC2[n_proj] / (1.0 - ECC2[n_proj]);
TransverseMercator[n_proj].t_c1 = (1.0 - 0.25 * ECC2[n_proj] - 3.0 * ECC4[n_proj] / 64.0 - 5.0 * ECC6[n_proj] / 256.0);
TransverseMercator[n_proj].t_c2 = (3.0 * ECC2[n_proj] / 8.0 + 3.0 * ECC4[n_proj] / 32.0 + 45.0 * ECC6[n_proj] / 1024.0);
TransverseMercator[n_proj].t_c3 = (15.0 * ECC4[n_proj] / 256.0 + 45.0 * ECC6[n_proj] / 1024.0);
TransverseMercator[n_proj].t_c4 = (35.0 * ECC6[n_proj] / 3072.0);
TransverseMercator[n_proj].t_ic1 = (1.5 * e1 - 27.0 * pow(e1, 3.0) / 32.0);
TransverseMercator[n_proj].t_ic2 = (21.0 * e1 * e1 / 16.0 - 55.0 * pow(e1, 4.0) / 32.0);
TransverseMercator[n_proj].t_ic3 = (151.0 * pow(e1, 3.0) / 96.0);
TransverseMercator[n_proj].t_ic4 = (1097.0 * pow(e1, 4.0) / 512.0);
TransverseMercator[n_proj].central_meridian = lon0;
TransverseMercator[n_proj].use_false_easting = use_false_easting;
TransverseMercator[n_proj].false_easting = false_easting;
TransverseMercator[n_proj].map_scale_factor = map_scale_factor;
// get y offset of central parallel (use lat0 = 0.0 for standard TM w/o offset)
double lon, lat, x, y;
lon = lon0;
lat = lat0;
TransverseMercator[n_proj].y_central_parallel = 0;
tm(n_proj, lon, lat, &x, &y);
TransverseMercator[n_proj].y_central_parallel = y;
}
void tm(n_proj, lon, lat, x, y)
int n_proj;
double lon, lat, *x, *y;
{
/* Convert lon/lat to TM x/y */
double N, T, T2, C, A, M, dlon, tan_lat, cos_lat, A2, A3, A5;
dlon = lon - TransverseMercator[n_proj].central_meridian;
if (fabs(dlon) > 360.0) dlon += copysign(360.0, -dlon);
if (fabs(dlon) > 180.0) dlon = copysign(360.0 - fabs(dlon), -dlon);
lat *= D2R;
M = EQ_RAD[n_proj] * (TransverseMercator[n_proj].t_c1 * lat - TransverseMercator[n_proj].t_c2 * sin(2.0 * lat)
+ TransverseMercator[n_proj].t_c3 * sin(4.0 * lat) - TransverseMercator[n_proj].t_c4 * sin(6.0 * lat));
if (fabs(lat) == M_PI_2) {
*x = 0.0;
*y = TransverseMercator[n_proj].map_scale_factor * M;
} else {
N = EQ_RAD[n_proj] / d_sqrt(1.0 - ECC2[n_proj] * pow(sin(lat), 2.0));
tan_lat = tan(lat);
cos_lat = cos(lat);
T = tan_lat * tan_lat;
T2 = T * T;
C = TransverseMercator[n_proj].t_e2 * cos_lat * cos_lat;
A = dlon * D2R * cos_lat;
A2 = A * A;
A3 = A2 * A;
A5 = A3 * A2;
*x = TransverseMercator[n_proj].map_scale_factor * N * (A + (1.0 - T + C) * (A3 * 0.16666666666666666667)
+ (5.0 - 18.0 * T + T2 + 72.0 * C - 58.0 * TransverseMercator[n_proj].t_e2) * (A5 * 0.00833333333333333333));
A3 *= A;
A5 *= A;
*y = TransverseMercator[n_proj].map_scale_factor * (M + N * tan(lat) * (0.5 * A2 + (5.0 - T + 9.0 * C + 4.0 * C * C) * (A3 * 0.04166666666666666667)
+ (61.0 - 58.0 * T + T2 + 600.0 * C - 330.0 * TransverseMercator[n_proj].t_e2) * (A5 * 0.00138888888888888889)));
}
// correct for TM x offset of central parallel
*y -= TransverseMercator[n_proj].y_central_parallel;
// correct for false easting
if (TransverseMercator[n_proj].use_false_easting) {
*x += TransverseMercator[n_proj].false_easting;
}
}
void itm(n_proj, lon, lat, x, y)
int n_proj;
double *lon, *lat, x, y;
{
// correct for TM x offset of central parallel
y += TransverseMercator[n_proj].y_central_parallel;
// correct for false easting
if (TransverseMercator[n_proj].use_false_easting) {
x -= TransverseMercator[n_proj].false_easting;
}
/* Convert TM x/y to lon/lat */
double M, mu, phi1, C1, C12, T1, T12, tmp, tmp2, N1, R1, D, D2, D3, D5, cos_phi1, tan_phi1;
M = y / TransverseMercator[n_proj].map_scale_factor;
mu = M / (EQ_RAD[n_proj] * TransverseMercator[n_proj].t_c1);
phi1 = mu + TransverseMercator[n_proj].t_ic1 * sin(2.0 * mu) + TransverseMercator[n_proj].t_ic2 * sin(4.0 * mu)
+ TransverseMercator[n_proj].t_ic3 * sin(6.0 * mu) + TransverseMercator[n_proj].t_ic4 * sin(8.0 * mu);
cos_phi1 = cos(phi1);
tan_phi1 = tan(phi1);
C1 = TransverseMercator[n_proj].t_e2 * cos_phi1 * cos_phi1;
C12 = C1 * C1;
T1 = tan_phi1 * tan_phi1;
T12 = T1 * T1;
tmp = 1.0 - ECC2[n_proj] * (1.0 - cos_phi1 * cos_phi1);
tmp2 = d_sqrt(tmp);
N1 = EQ_RAD[n_proj] / tmp2;
R1 = EQ_RAD[n_proj] * (1.0 - ECC2[n_proj]) / (tmp * tmp2);
D = x / (N1 * TransverseMercator[n_proj].map_scale_factor);
D2 = D * D;
D3 = D2 * D;
D5 = D3 * D2;
*lon = TransverseMercator[n_proj].central_meridian + R2D * (D - (1.0 + 2.0 * T1 + C1) * (D3 * 0.16666666666666666667)
+ (5.0 - 2.0 * C1 + 28.0 * T1 - 3.0 * C12 + 8.0 * TransverseMercator[n_proj].t_e2 + 24.0 * T12)
* (D5 * 0.00833333333333333333)) / cos(phi1);
D3 *= D;
D5 *= D;
*lat = phi1 - (N1 * tan(phi1) / R1) * (0.5 * D2 -
(5.0 + 3.0 * T1 + 10.0 * C1 - 4.0 * C12 - 9.0 * TransverseMercator[n_proj].t_e2) * (D3 * 0.04166666666666666667)
+ (61.0 + 90.0 * T1 + 298 * C1 + 45.0 * T12 - 252.0 * TransverseMercator[n_proj].t_e2 - 3.0 * C12) * (D5 * 0.00138888888888888889));
(*lat) *= R2D;
}
/*
* TRANSFORMATION ROUTINES FOR THE Universal Transverse Mercator Projection (UTM)
*/
/*
int map_init_utm(int n_proj) {
BOOLEAN search;
double xmin, xmax, ymin, ymax, lon0;
lon0 = 180.0 + 6.0 * TransverseMercator[n_proj].pars[0] - 3.0;
if (lon0 >= 360.0) lon0 -= 360.0;
vtm(lon0); // Central meridian for this zone
if (TransverseMercator[n_proj].units_pr_degree) TransverseMercator[n_proj].pars[1] /= M_PR_DEG;
TransverseMercator[n_proj].x_scale = TransverseMercator[n_proj].y_scale = TransverseMercator[n_proj].pars[1];
forward = (PFI) utm;
inverse = (PFI) iutm;
if (fabs(TransverseMercator[n_proj].w - TransverseMercator[n_proj].e) > 360.0) { // -R in UTM meters
iutm(&TransverseMercator[n_proj].w, &TransverseMercator[n_proj].s, TransverseMercator[n_proj].w, TransverseMercator[n_proj].s);
iutm(&TransverseMercator[n_proj].e, &TransverseMercator[n_proj].n, TransverseMercator[n_proj].e, TransverseMercator[n_proj].n);
TransverseMercator[n_proj].region = FALSE;
}
if (TransverseMercator[n_proj].region) {
xy_search(&xmin, &xmax, &ymin, &ymax);
outside = (PFI) wesn_outside;
crossing = (PFI) wesn_crossing;
overlap = (PFI) wesn_overlap;
map_clip = (PFI) wesn_clip;
left_edge = (PFD) left_rect;
right_edge = (PFD) right_rect;
search = FALSE;
} else {
utm(TransverseMercator[n_proj].w, TransverseMercator[n_proj].s, &xmin, &ymin);
utm(TransverseMercator[n_proj].e, TransverseMercator[n_proj].n, &xmax, &ymax);
outside = (PFI) rect_outside;
crossing = (PFI) rect_crossing;
overlap = (PFI) rect_overlap;
map_clip = (PFI) rect_clip;
left_edge = (PFD) left_rect;
right_edge = (PFD) right_rect;
frame_info.check_side = TRUE;
search = TRUE;
}
frame_info.horizontal = TRUE;
map_setxy(xmin, xmax, ymin, ymax);
gmtdefs.basemap_type = 1;
return (search);
}*/
void utm(n_proj, lon, lat, x, y)
int n_proj;
double lon, lat, *x, *y;
{
/* Convert lon/lat to UTM x/y */
if (lon < 0.0) lon += 360.0;
tm(n_proj, lon, lat, x, y);
(*x) += 500000.0;
if (!TransverseMercator[n_proj].north_pole)
(*y) += 10000000.0; /* For S hemisphere, add 10^6 m */
}
void iutm(n_proj, lon, lat, x, y)
int n_proj;
double *lon, *lat, x, y;
{
/* Convert UTM x/y to lon/lat */
x -= 500000.0;
if (!TransverseMercator[n_proj].north_pole)
y -= 10000000.0;
itm(n_proj, lon, lat, x, y);
}
// utm init function
// created by ALomax to enable setting of north_pole flag
void vutm(int n_proj, double lon0, int lat0, int use_false_easting, long false_easting, double map_scale_factor) {
vtm(n_proj, lon0, lat0, use_false_easting, false_easting, map_scale_factor);
TransverseMercator[n_proj].north_pole = lat0 >= 0.0 ? 1 : 0;
}
/* Azimuthal Equidistant Projection (AE) */
struct AZIMUTHAL_EQUIDIST {
BOOLEAN north_pole; /* TRUE if projection is on northern hemisphere, FALSE on southern */
double central_meridian; // Central meridian (longitude) for projection
double pole; // Central latitude for projection
double sinp;
double cosp;
};
struct AZIMUTHAL_EQUIDIST AzimuthalEquidistant[NUM_PROJ_MAX];
/*
* TRANSFORMATION ROUTINES FOR THE AZIMUTHAL EQUIDISTANT PROJECTION
*/
/*
int map_init_azeqdist () {
BOOLEAN search;
double xmin, xmax, ymin, ymax, dummy, radius;
gmt_set_spherical (); // PW: Force spherical for now
if (project_info.units_pr_degree) {
vazeqdist (0.0, 90.0);
azeqdist (0.0, project_info.pars[3], &dummy, &radius);
if (radius == 0.0) radius = M_PI * EQ_RAD;
project_info.x_scale = project_info.y_scale = fabs (project_info.pars[2] / radius);
}
else
project_info.x_scale = project_info.y_scale = project_info.pars[2];
vazeqdist (project_info.pars[0], project_info.pars[1]);
forward = (PFI)azeqdist; inverse = (PFI)iazeqdist;
if (fabs (project_info.pars[1]) == 90.0) {
project_info.polar = TRUE;
project_info.north_pole = (project_info.pars[1] == 90.0);
}
if (!project_info.region) { // Rectangular box given
(*forward) (project_info.w, project_info.s, &xmin, &ymin);
(*forward) (project_info.e, project_info.n, &xmax, &ymax);
outside = (PFI) rect_outside;
crossing = (PFI) rect_crossing;
overlap = (PFI) rect_overlap;
map_clip = (PFI) rect_clip;
left_edge = (PFD) left_rect;
right_edge = (PFD) right_rect;
frame_info.check_side = !gmtdefs.oblique_anotation;
frame_info.horizontal = (fabs (project_info.pars[1]) < 60.0 && fabs (project_info.n - project_info.s) < 30.0);
search = TRUE;
}
else {
if (project_info.polar && (project_info.n - project_info.s) < 180.0) { // Polar aspect
if (!project_info.north_pole && project_info.s == -90.0) project_info.edge[0] = FALSE;
if (project_info.north_pole && project_info.n == 90.0) project_info.edge[2] = FALSE;
if ((fabs (project_info.w - project_info.e) == 360.0 || project_info.w == project_info.e)) project_info.edge[1] = project_info.edge[3] = FALSE;
outside = (PFI) polar_outside;
crossing = (PFI) wesn_crossing;
overlap = (PFI) wesn_overlap;
map_clip = (PFI) wesn_clip;
frame_info.horizontal = TRUE;
gmtdefs.n_lat_nodes = 2;
xy_search (&xmin, &xmax, &ymin, &ymax);
}
else { // Global view only, force wesn = 0/360/-90/90
frame_info.anot_int[0] = frame_info.anot_int[1] = 0.0; // No annotations for global mode
frame_info.frame_int[0] = frame_info.frame_int[1] = 0.0; // No tickmarks for global mode
project_info.w = 0.0;
project_info.e = 360.0;
project_info.s = -90.0;
project_info.n = 90.0;
xmin = ymin = -M_PI * EQ_RAD;
xmax = ymax = -xmin;
outside = (PFI) eqdist_outside;
crossing = (PFI) eqdist_crossing;
overlap = (PFI) radial_overlap;
map_clip = (PFI) radial_clip;
gmtdefs.basemap_type = 1;
}
search = FALSE;
left_edge = (PFD) left_circle;
right_edge = (PFD) right_circle;
}
map_setinfo (xmin, xmax, ymin, ymax, project_info.pars[2]);
project_info.r = 0.5 * project_info.xmax;
return (search);
}*/
void vazeqdist(int n_proj, double lon0, double lat0) {
/* Set up azimuthal equidistant projection */
AzimuthalEquidistant[n_proj].central_meridian = lon0;
AzimuthalEquidistant[n_proj].pole = lat0;
AzimuthalEquidistant[n_proj].sinp = sin(lat0 * D2R);
AzimuthalEquidistant[n_proj].cosp = cos(lat0 * D2R);
}
void azeqdist(int n_proj, double lon, double lat, double *x, double *y) {
/* Convert lon/lat to azimuthal equidistant x/y */
double k, dlon, cc, c, clat, clon, slat;
while ((lon - AzimuthalEquidistant[n_proj].central_meridian) < -180.0) lon += 360.0;
while ((lon - AzimuthalEquidistant[n_proj].central_meridian) > 180.0) lon -= 360.0;
dlon = (lon - AzimuthalEquidistant[n_proj].central_meridian) * D2R;
lat *= D2R;
slat = sin(lat);
clat = cos(lat);
clon = cos(dlon);
cc = AzimuthalEquidistant[n_proj].sinp * slat + AzimuthalEquidistant[n_proj].cosp * clat * clon;
if (fabs(cc) >= 1.0)
*x = *y = 0.0;
else {
c = acos(cc);
k = EQ_RAD[n_proj] * c / sin(c);
*x = k * clat * sin(dlon);
*y = k * (AzimuthalEquidistant[n_proj].cosp * slat - AzimuthalEquidistant[n_proj].sinp * clat * clon);
}
}
void iazeqdist(int n_proj, double *lon, double *lat, double x, double y) {
/* Convert azimuthal equidistant x/yto lon/lat */
double rho, c, sin_c, cos_c;
rho = hypot(x, y);
if (rho == 0.0) {
*lat = AzimuthalEquidistant[n_proj].pole;
*lon = AzimuthalEquidistant[n_proj].central_meridian;
} else {
c = rho / EQ_RAD[n_proj];
sin_c = sin(c);
cos_c = cos(c);
*lat = asin(cos_c * AzimuthalEquidistant[n_proj].sinp + (y * sin_c * AzimuthalEquidistant[n_proj].cosp / rho)) * R2D;
if (AzimuthalEquidistant[n_proj].pole == 90.0)
*lon = AzimuthalEquidistant[n_proj].central_meridian + R2D * atan2(x, -y);
else if (AzimuthalEquidistant[n_proj].pole == -90.0)
*lon = AzimuthalEquidistant[n_proj].central_meridian + R2D * atan2(x, y);
else
*lon = AzimuthalEquidistant[n_proj].central_meridian +
R2D * atan2(x * sin_c, (rho * AzimuthalEquidistant[n_proj].cosp * cos_c - y * AzimuthalEquidistant[n_proj].sinp * sin_c));
if ((*lon) <= -180) (*lon) += 360.0;
}
}