| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
=head1 NAME |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
PDLA::Transform::Cartography - Useful cartographic projections |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# make a Mercator map of Earth |
|
8
|
|
|
|
|
|
|
use PDLA::Transform::Cartography; |
|
9
|
|
|
|
|
|
|
$x = earth_coast(); |
|
10
|
|
|
|
|
|
|
$x = graticule(10,2)->glue(1,$x); |
|
11
|
|
|
|
|
|
|
$t = t_mercator; |
|
12
|
|
|
|
|
|
|
$w = pgwin(xs); |
|
13
|
|
|
|
|
|
|
$w->lines($t->apply($x)->clean_lines()); |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
PDLA::Transform::Cartography includes a variety of useful cartographic |
|
18
|
|
|
|
|
|
|
and observing projections (mappings of the surface of a sphere), |
|
19
|
|
|
|
|
|
|
including reprojected observer coordinates. See L |
|
20
|
|
|
|
|
|
|
for more information about image transforms in general. |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Cartographic transformations are used for projecting not just |
|
23
|
|
|
|
|
|
|
terrestrial maps, but also any nearly spherical surface including the |
|
24
|
|
|
|
|
|
|
Sun, the Celestial sphere, various moons and planets, distant stars, |
|
25
|
|
|
|
|
|
|
etc. They also are useful for interpreting scientific images, which |
|
26
|
|
|
|
|
|
|
are themselves generally projections of a sphere onto a flat focal |
|
27
|
|
|
|
|
|
|
plane (e.g. the L projection). |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Unless otherwise noted, all the transformations in this file convert |
|
30
|
|
|
|
|
|
|
from (theta,phi) coordinates on the unit sphere (e.g. (lon,lat) on a |
|
31
|
|
|
|
|
|
|
planet or (RA,dec) on the celestial sphere) into some sort of |
|
32
|
|
|
|
|
|
|
projected coordinates, and have inverse transformations that convert |
|
33
|
|
|
|
|
|
|
back to (theta,phi). This is equivalent to working from the |
|
34
|
|
|
|
|
|
|
equidistant cylindrical (or L<"plate caree"|/t_caree>) projection, if |
|
35
|
|
|
|
|
|
|
you are a cartography wonk. |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
The projected coordinates are generally in units of body radii |
|
38
|
|
|
|
|
|
|
(radians), so that multiplying the output by the scale of the map |
|
39
|
|
|
|
|
|
|
yields physical units that are correct wherever the scale is correct |
|
40
|
|
|
|
|
|
|
for that projection. For example, areas should be correct everywhere |
|
41
|
|
|
|
|
|
|
in the authalic projections; and linear scales are correct along |
|
42
|
|
|
|
|
|
|
meridians in the equidistant projections and along the standard |
|
43
|
|
|
|
|
|
|
parallels in all the projections. |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
The transformations that are authalic (equal-area), conformal |
|
46
|
|
|
|
|
|
|
(equal-angle), azimuthal (circularly symmetric), or perspective (true |
|
47
|
|
|
|
|
|
|
perspective on a focal plane from some viewpoint) are marked. The |
|
48
|
|
|
|
|
|
|
first two categories are mutually exclusive for all but the |
|
49
|
|
|
|
|
|
|
L<"unit sphere"|/t_unit_sphere> 3-D projection. |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Extra dimensions tacked on to each point to be transformed are, in |
|
52
|
|
|
|
|
|
|
general, ignored. That is so that you can add on an extra index |
|
53
|
|
|
|
|
|
|
to keep track of pen color. For example, L |
|
54
|
|
|
|
|
|
|
returns a 3x piddle containing (lon, lat, pen) at each list location. |
|
55
|
|
|
|
|
|
|
Transforming the vector list retains the pen value as the first index |
|
56
|
|
|
|
|
|
|
after the dimensional directions. |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 GENERAL NOTES ON CARTOGRAPHY |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Unless otherwise noted, the transformations and miscellaneous |
|
61
|
|
|
|
|
|
|
information in this section are taken from Snyder & Voxland 1989: "An |
|
62
|
|
|
|
|
|
|
Album of Map Projections", US Geological Survey Professional Paper |
|
63
|
|
|
|
|
|
|
1453, US Printing Office (Denver); and from Snyder 1987: "Map |
|
64
|
|
|
|
|
|
|
Projections - A Working Manual", US Geological Survey Professional |
|
65
|
|
|
|
|
|
|
Paper 1395, US Printing Office (Denver, USA). You can obtain your own |
|
66
|
|
|
|
|
|
|
copy of both by contacting the U.S. Geological Survey, Federal Center, |
|
67
|
|
|
|
|
|
|
Box 25425, Denver, CO 80225 USA. |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
The mathematics of cartography have a long history, and the details |
|
70
|
|
|
|
|
|
|
are far trickier than the broad overview. For terrestrial (and, in |
|
71
|
|
|
|
|
|
|
general, planetary) cartography, the best reference datum is not a |
|
72
|
|
|
|
|
|
|
sphere but an oblate ellipsoid due to centrifugal force from the |
|
73
|
|
|
|
|
|
|
planet's rotation. Furthermore, because all rocky planets, including |
|
74
|
|
|
|
|
|
|
Earth, have randomly placed mass concentrations that affect the |
|
75
|
|
|
|
|
|
|
gravitational field, the reference gravitational isosurface (sea level |
|
76
|
|
|
|
|
|
|
on Earth) is even more complex than an ellipsoid and, in general, |
|
77
|
|
|
|
|
|
|
different ellipsoids have been used for different locations at the |
|
78
|
|
|
|
|
|
|
same time and for the same location at different times. |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
The transformations in this package use a spherical datum and hence |
|
81
|
|
|
|
|
|
|
include global distortion at about the 0.5% level for terrestrial maps |
|
82
|
|
|
|
|
|
|
(Earth's oblateness is ~1/300). This is roughly equal to the |
|
83
|
|
|
|
|
|
|
dimensional precision of physical maps printed on paper (due to |
|
84
|
|
|
|
|
|
|
stretching and warping of the paper) but is significant at larger |
|
85
|
|
|
|
|
|
|
scales (e.g. for regional maps). If you need more precision than |
|
86
|
|
|
|
|
|
|
that, you will want to implement and use the ellipsoidal |
|
87
|
|
|
|
|
|
|
transformations from Snyder 1987 or another reference work on geodesy. |
|
88
|
|
|
|
|
|
|
A good name for that package would be C<...::Cartography::Geodetic>. |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head1 GENERAL NOTES ON PERSPECTIVE AND SCIENTIFIC IMAGES |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
Cartographic transformations are useful for interpretation of |
|
93
|
|
|
|
|
|
|
scientific images, as all cameras produce projections of the celestial |
|
94
|
|
|
|
|
|
|
sphere onto the focal plane of the camera. A simple (single-element) |
|
95
|
|
|
|
|
|
|
optical system with a planar focal plane generates |
|
96
|
|
|
|
|
|
|
L images -- that is to say, gnomonic projections |
|
97
|
|
|
|
|
|
|
of a portion of the celestial sphere near the paraxial direction. |
|
98
|
|
|
|
|
|
|
This is the projection that most consumer grade cameras produce. |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Magnification in an optical system changes the angle of incidence |
|
101
|
|
|
|
|
|
|
of the rays on the focal plane for a given angle of incidence at the |
|
102
|
|
|
|
|
|
|
aperture. For example, a 10x telescope with a 2 degree field of view |
|
103
|
|
|
|
|
|
|
exhibits the same gnomonic distortion as a simple optical system with |
|
104
|
|
|
|
|
|
|
a 20 degree field of view. Wide-angle optics typically have magnification |
|
105
|
|
|
|
|
|
|
less than 1 ('fisheye lenses'), reducing the gnomonic distortion |
|
106
|
|
|
|
|
|
|
considerably but introducing L<"equidistant azimuthal"|/t_az_eqd> distortion -- |
|
107
|
|
|
|
|
|
|
there's no such thing as a free lunch! |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Because many solar-system objects are spherical, |
|
110
|
|
|
|
|
|
|
PDLA::Transform::Cartography includes perspective projections for |
|
111
|
|
|
|
|
|
|
producing maps of spherical bodies from perspective views. Those |
|
112
|
|
|
|
|
|
|
projections are L<"t_vertical"|/t_vertical> and |
|
113
|
|
|
|
|
|
|
L<"t_perspective"|/t_perspective>. They map between (lat,lon) on the |
|
114
|
|
|
|
|
|
|
spherical body and planar projected coordinates at the viewpoint. |
|
115
|
|
|
|
|
|
|
L<"t_vertical"|/t_vertical> is the vertical perspective projection |
|
116
|
|
|
|
|
|
|
given by Snyder, but L<"t_perspective"|/t_perspective> is a fully |
|
117
|
|
|
|
|
|
|
general perspective projection that also handles magnification correction. |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head1 TRANSVERSE & OBLIQUE PROJECTIONS; STANDARD OPTIONS |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Oblique projections rotate the sphere (and graticule) to an arbitrary |
|
122
|
|
|
|
|
|
|
angle before generating the projection; transverse projections rotate |
|
123
|
|
|
|
|
|
|
the sphere exactly 90 degrees before generating the projection. |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Most of the projections accept the following standard options, |
|
126
|
|
|
|
|
|
|
useful for making transverse and oblique projection maps. |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=over 3 |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=item o, origin, Origin [default (0,0,0)] |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
The origin of the oblique map coordinate system, in (old-theta, old-phi) |
|
133
|
|
|
|
|
|
|
coordinates. |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item r, roll, Roll [default 0.0] |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
The roll angle of the sphere about the origin, measured CW from (N = up) |
|
138
|
|
|
|
|
|
|
for reasonable values of phi and CW from (S = up) for unreasonable |
|
139
|
|
|
|
|
|
|
values of phi. This is equivalent to observer roll angle CCW from the |
|
140
|
|
|
|
|
|
|
same direction. |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=item u, unit, Unit [default 'degree'] |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
This is the name of the angular unit to use in the lon/lat coordinate system. |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=item b, B |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
The "B" angle of the body -- used for extraterrestrial maps. Setting |
|
149
|
|
|
|
|
|
|
this parameter is exactly equivalent to setting the phi component |
|
150
|
|
|
|
|
|
|
of the origin, and in fact overrides it. |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=item l,L |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
The longitude of the central meridian as observed -- used for extraterrestrial |
|
155
|
|
|
|
|
|
|
maps. Setting this parameter is exactly equivalent to setting the theta |
|
156
|
|
|
|
|
|
|
component of the origin, and in fact overrides it. |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=item p,P |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
The "P" (or position) angle of the body -- used for extraterrestrial maps. |
|
161
|
|
|
|
|
|
|
This parameter is a synonym for the roll angle, above. |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=item bad, Bad, missing, Missing [default nan] |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
This is the value that missing points get. Mainly useful for the |
|
166
|
|
|
|
|
|
|
inverse transforms. (This should work fine if set to BAD, if you have |
|
167
|
|
|
|
|
|
|
bad-value support compiled in). The default nan is asin(1.2), calculated |
|
168
|
|
|
|
|
|
|
at load time. |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=back |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head1 EXAMPLES |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
Draw a Mercator map of the world on-screen: |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
$w = pgwin(xs); |
|
177
|
|
|
|
|
|
|
$w->lines(earth_coast->apply(t_mercator)->clean_lines); |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Here, C returns a 3xn piddle containing (lon, lat, pen) |
|
180
|
|
|
|
|
|
|
values for the included world coastal outline; C converts |
|
181
|
|
|
|
|
|
|
the values to projected Mercator coordinates, and C breaks |
|
182
|
|
|
|
|
|
|
lines that cross the 180th meridian. |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
Draw a Mercator map of the world, with lon/lat at 10 degree intervals: |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
$w = pgwin(xs) |
|
187
|
|
|
|
|
|
|
$x = earth_coast()->glue(1,graticule(10,1)); |
|
188
|
|
|
|
|
|
|
$w->lines($x->apply(t_mercator)->clean_lines); |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
This works just the same as the first example, except that a map graticule |
|
191
|
|
|
|
|
|
|
has been applied with interline spacing of 10 degrees lon/lat and |
|
192
|
|
|
|
|
|
|
inter-vertex spacing of 1 degree (so that each meridian contains 181 points, |
|
193
|
|
|
|
|
|
|
and each parallel contains 361 points). |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=head1 NOTES |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
Currently angular conversions are rather simpleminded. A list of |
|
198
|
|
|
|
|
|
|
common conversions is present in the main constructor, which inserts a |
|
199
|
|
|
|
|
|
|
conversion constant to radians into the {params} field of the new |
|
200
|
|
|
|
|
|
|
transform. Something like Math::Convert::Units should be used instead |
|
201
|
|
|
|
|
|
|
to generate the conversion constant. |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
A cleaner higher-level interface is probably needed (see the examples); |
|
204
|
|
|
|
|
|
|
for example, earth_coast could return a graticule if asked, instead of |
|
205
|
|
|
|
|
|
|
needing one to be glued on. |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
The class structure is somewhat messy because of the varying needs of |
|
208
|
|
|
|
|
|
|
the different transformations. PDLA::Transform::Cartography is a base |
|
209
|
|
|
|
|
|
|
class that interprets the origin options and sets up the basic |
|
210
|
|
|
|
|
|
|
machinery of the Transform. The conic projections have their |
|
211
|
|
|
|
|
|
|
own subclass, PDLA::Transform::Conic, that interprets the standard |
|
212
|
|
|
|
|
|
|
parallels. Since the cylindrical and azimuthal projections are pretty |
|
213
|
|
|
|
|
|
|
simple, they are not subclassed. |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
The perl 5.6.1 compiler is quite slow at adding new classes to the |
|
216
|
|
|
|
|
|
|
structure, so it does not makes sense to subclass new transformations |
|
217
|
|
|
|
|
|
|
merely for the sake of pedantry. |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head1 AUTHOR |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Copyright 2002, Craig DeForest (deforest@boulder.swri.edu). This |
|
222
|
|
|
|
|
|
|
module may be modified and distributed under the same terms as PDLA |
|
223
|
|
|
|
|
|
|
itself. The module comes with NO WARRANTY. |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
The included digital world map is derived from the 1987 CIA World Map, |
|
226
|
|
|
|
|
|
|
translated to ASCII in 1988 by Joe Dellinger (geojoe@freeusp.org) and |
|
227
|
|
|
|
|
|
|
simplified in 1995 by Kirk Johnson (tuna@indra.com) for the program |
|
228
|
|
|
|
|
|
|
XEarth. The map comes with NO WARRANTY. An ASCII version of the map, |
|
229
|
|
|
|
|
|
|
and a sample PDLA function to read it, may be found in the Demos |
|
230
|
|
|
|
|
|
|
subdirectory of the PDLA source distribution. |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
The module exports both transform constructors ('t_') and some |
|
235
|
|
|
|
|
|
|
auxiliary functions (no leading 't_'). |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=cut |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Import PDLA::Transform into the calling package -- the cartography |
|
240
|
|
|
|
|
|
|
# stuff isn't much use without it. |
|
241
|
1
|
|
|
1
|
|
74796
|
use PDLA::Transform; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
8
|
|
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
package PDLA::Transform::Cartography; |
|
244
|
1
|
|
|
1
|
|
264
|
use PDLA::Core ':Internal'; # Load 'topdl' (internal routine) |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
19
|
|
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
@ISA = ( 'Exporter','PDLA::Transform' ); |
|
247
|
|
|
|
|
|
|
our $VERSION = "0.6"; |
|
248
|
|
|
|
|
|
|
$VERSION = eval $VERSION; |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
BEGIN { |
|
251
|
1
|
|
|
1
|
|
235
|
use Exporter (); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
234
|
|
|
252
|
1
|
|
|
1
|
|
7
|
@EXPORT_OK = qw(graticule earth_image earth_coast clean_lines t_unit_sphere t_orthographic t_rot_sphere t_caree t_mercator t_utm t_sin_lat t_sinusoidal t_conic t_albers t_lambert t_stereographic t_gnomonic t_az_eqd t_az_eqa t_vertical t_perspective t_hammer t_aitoff); |
|
253
|
1
|
|
|
|
|
4
|
@EXPORT = @EXPORT_OK; |
|
254
|
1
|
|
|
|
|
32
|
%EXPORT_TAGS = (Func=>[@EXPORT_OK]); |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
1
|
|
|
1
|
|
7
|
use PDLA; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
10
|
|
|
258
|
1
|
|
|
1
|
|
3210
|
use PDLA::Transform; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
4
|
|
|
259
|
1
|
|
|
1
|
|
238
|
use PDLA::MatrixOps; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
4
|
|
|
260
|
1
|
|
|
1
|
|
107
|
use PDLA::NiceSlice; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
19
|
|
|
261
|
|
|
|
|
|
|
|
|
262
|
1
|
|
|
1
|
|
155382
|
use Carp; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
131
|
|
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
############################## |
|
266
|
|
|
|
|
|
|
# Steal _opt from PDLA::Transform. |
|
267
|
|
|
|
|
|
|
*PDLA::Transform::Cartography::_opt = \&PDLA::Transform::_opt; |
|
268
|
1
|
|
|
1
|
|
11
|
use overload '""' => \&_strval; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
25
|
|
|
269
|
|
|
|
|
|
|
|
|
270
|
1
|
|
|
1
|
|
70
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
10708
|
|
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
our $PI = $PDLA::Transform::PI; |
|
273
|
|
|
|
|
|
|
our $DEG2RAD = $PDLA::Transform::DEG2RAD; |
|
274
|
|
|
|
|
|
|
our $RAD2DEG = $PDLA::Transform::RAD2DEG; |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
sub _strval { |
|
277
|
0
|
|
|
0
|
|
0
|
my($me) = shift; |
|
278
|
0
|
|
|
|
|
0
|
$me->stringify(); |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
###################################################################### |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=head2 graticule |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=for usage |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
$lonlatp = graticule(,); |
|
288
|
|
|
|
|
|
|
$lonlatp = graticule(,,1); |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=for ref |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
(Cartography) PDLA constructor - generate a lat/lon grid. |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Returns a grid of meridians and parallels as a list of vectors suitable |
|
295
|
|
|
|
|
|
|
for sending to |
|
296
|
|
|
|
|
|
|
L |
|
297
|
|
|
|
|
|
|
for plotting. |
|
298
|
|
|
|
|
|
|
The grid is in degrees in (theta, phi) coordinates -- this is (E lon, N lat) |
|
299
|
|
|
|
|
|
|
for terrestrial grids or (RA, dec) for celestial ones. You must then |
|
300
|
|
|
|
|
|
|
transform the graticule in the same way that you transform the map. |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
You can attach the graticule to a vector map using the syntax: |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
$out = graticule(10,2)->glue(1,$map); |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
In array context you get back a 2-element list containing a piddle of |
|
307
|
|
|
|
|
|
|
the (theta,phi) pairs and a piddle of the pen values (1 or 0) suitable for |
|
308
|
|
|
|
|
|
|
calling |
|
309
|
|
|
|
|
|
|
L. |
|
310
|
|
|
|
|
|
|
In scalar context the two elements are combined into a single piddle. |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
The pen values associated with the graticule are negative, which will cause |
|
313
|
|
|
|
|
|
|
L |
|
314
|
|
|
|
|
|
|
to plot them as hairlines. |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
If a third argument is given, it is a hash of options, which can be: |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=over 3 |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item nan - if true, use two columns instead of three, and separate lines with a 'nan' break |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=item lonpos - if true, all reported longitudes are positive (0 to 360) instead of (-180 to 180). |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=item dup - if true, the meridian at the far boundary is duplicated. |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=back |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=cut |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub graticule { |
|
331
|
0
|
|
|
0
|
1
|
0
|
my $grid = shift; |
|
332
|
0
|
|
|
|
|
0
|
my $step = shift; |
|
333
|
0
|
0
|
|
|
|
0
|
my $hash = shift; $hash = {} unless defined($hash); # avoid // for ancient compatibility |
|
|
0
|
|
|
|
|
0
|
|
|
334
|
0
|
|
0
|
|
|
0
|
my $two_cols = $hash->{nan} || 0; |
|
335
|
0
|
|
0
|
|
|
0
|
my $lonpos = $hash->{lonpos} || 0; |
|
336
|
0
|
|
0
|
|
|
0
|
my $dup = $hash->{dup} || 0; |
|
337
|
|
|
|
|
|
|
|
|
338
|
0
|
0
|
|
|
|
0
|
$grid = 10 unless defined($grid); |
|
339
|
0
|
0
|
|
|
|
0
|
$grid = $grid->at(0) if(ref($grid) eq 'PDLA'); |
|
340
|
|
|
|
|
|
|
|
|
341
|
0
|
0
|
|
|
|
0
|
$step = $grid/2 unless defined($step); |
|
342
|
0
|
0
|
|
|
|
0
|
$step = $step->at(0) if(ref($step) eq 'PDLA'); |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# Figure number of parallels and meridians |
|
345
|
0
|
|
|
|
|
0
|
my $np = 2 * int(90/$grid); |
|
346
|
0
|
|
|
|
|
0
|
my $nm = 2 * int(180/$grid); |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# First do parallels. |
|
349
|
0
|
|
|
|
|
0
|
my $xp = xvals(360/$step + 1 + !!$two_cols, $np + 1) * $step - 180 * (!$lonpos); |
|
350
|
0
|
|
|
|
|
0
|
my $yp = yvals(360/$step + 1 + !!$two_cols, $np + 1) * 180/$np - 90; |
|
351
|
0
|
0
|
|
|
|
0
|
$xp->(-1,:) .= $yp->(-1,:) .= asin(pdl(1.1)) if($two_cols); |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
# Next do meridians. |
|
354
|
0
|
|
|
|
|
0
|
my $xm = yvals( 180/$step + 1 + !!$two_cols, $nm + !!$dup ) * 360/$nm - 180 * (!$lonpos); |
|
355
|
0
|
|
|
|
|
0
|
my $ym = xvals( 180/$step + 1 + !!$two_cols, $nm + !!$dup ) * $step - 90; |
|
356
|
0
|
0
|
|
|
|
0
|
$xm->(-1,:) .= $ym->(-1,:) .= asin(pdl(1.1)) if($two_cols); |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
|
|
359
|
0
|
0
|
|
|
|
0
|
if($two_cols) { |
|
360
|
0
|
|
|
|
|
0
|
return pdl( $xp->flat->append($xm->flat), |
|
361
|
|
|
|
|
|
|
$yp->flat->append($ym->flat) |
|
362
|
|
|
|
|
|
|
)->mv(1,0); |
|
363
|
|
|
|
|
|
|
} else { |
|
364
|
0
|
|
|
|
|
0
|
our $pp = (zeroes($xp)-1); $pp->((-1)) .= 0; |
|
|
0
|
|
|
|
|
0
|
|
|
365
|
0
|
|
|
|
|
0
|
our $pm = (zeroes($xm)-1); $pm->((-1)) .= 0; |
|
|
0
|
|
|
|
|
0
|
|
|
366
|
|
|
|
|
|
|
|
|
367
|
0
|
0
|
|
|
|
0
|
if(wantarray) { |
|
368
|
0
|
|
|
|
|
0
|
return ( pdl( $xp->flat->append($xm->flat), |
|
369
|
|
|
|
|
|
|
$yp->flat->append($ym->flat) |
|
370
|
|
|
|
|
|
|
)->mv(1,0), |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
$pp->flat->append($pm->flat) |
|
373
|
|
|
|
|
|
|
); |
|
374
|
|
|
|
|
|
|
} else { |
|
375
|
0
|
|
|
|
|
0
|
return pdl( $xp->flat->append($xm->flat), |
|
376
|
|
|
|
|
|
|
$yp->flat->append($ym->flat), |
|
377
|
|
|
|
|
|
|
$pp->flat->append($pm->flat) |
|
378
|
|
|
|
|
|
|
)->mv(1,0); |
|
379
|
|
|
|
|
|
|
} |
|
380
|
0
|
|
|
|
|
0
|
barf "This can't happen"; |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=head2 earth_coast |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=for usage |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
$x = earth_coast() |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=for ref |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
(Cartography) PDLA constructor - coastline map of Earth |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
Returns a vector coastline map based on the 1987 CIA World Coastline |
|
395
|
|
|
|
|
|
|
database (see author information). The vector coastline data are in |
|
396
|
|
|
|
|
|
|
plate caree format so they can be converted to other projections via |
|
397
|
|
|
|
|
|
|
the L method and cartographic transforms, |
|
398
|
|
|
|
|
|
|
and are suitable for plotting with the |
|
399
|
|
|
|
|
|
|
L |
|
400
|
|
|
|
|
|
|
method in the PGPLOT |
|
401
|
|
|
|
|
|
|
output library: the first dimension is (X,Y,pen) with breaks having |
|
402
|
|
|
|
|
|
|
a pen value of 0 and hairlines having negative pen values. The second |
|
403
|
|
|
|
|
|
|
dimension threads over all the points in the data set. |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
The vector map includes lines that pass through the antipodean |
|
406
|
|
|
|
|
|
|
meridian, so if you want to plot it without reprojecting, you should |
|
407
|
|
|
|
|
|
|
run it through L first: |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
$w = pgwin(); |
|
410
|
|
|
|
|
|
|
$w->lines(earth_coast->clean_lines); # plot plate caree map of world |
|
411
|
|
|
|
|
|
|
$w->lines(earth_coast->apply(t_gnomonic))# plot gnomonic map of world |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
C is just a quick-and-dirty way of loading the file |
|
414
|
|
|
|
|
|
|
"earth_coast.vec.fits" that is part of the normal installation tree. |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=cut |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
sub earth_coast { |
|
419
|
0
|
|
|
0
|
1
|
0
|
my $fn = "PDLA/Transform/Cartography/earth_coast.vec.fits"; |
|
420
|
0
|
|
|
|
|
0
|
local $_; |
|
421
|
0
|
|
|
|
|
0
|
foreach(@INC) { |
|
422
|
0
|
|
|
|
|
0
|
my $file = "$_/$fn"; |
|
423
|
0
|
0
|
|
|
|
0
|
return rfits($file) if(-e $file); |
|
424
|
|
|
|
|
|
|
} |
|
425
|
0
|
|
|
|
|
0
|
barf("earth_coast: $fn not found in \@INC.\n"); |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head2 earth_image |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=for usage |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
$rgb = earth_image() |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=for ref |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
(Cartography) PDLA constructor - RGB pixel map of Earth |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
Returns an RGB image of Earth based on data from the MODIS instrument |
|
439
|
|
|
|
|
|
|
on the NASA EOS/Terra satellite. (You can get a full-resolution |
|
440
|
|
|
|
|
|
|
image from L). |
|
441
|
|
|
|
|
|
|
The image is a plate caree map, so you can convert it to other |
|
442
|
|
|
|
|
|
|
projections via the L |
|
443
|
|
|
|
|
|
|
transforms. |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
This is just a quick-and-dirty way of loading the earth-image files that |
|
446
|
|
|
|
|
|
|
are distributed along with PDLA. |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=cut |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
sub earth_image { |
|
451
|
0
|
|
|
0
|
1
|
0
|
my($nd) = shift; |
|
452
|
0
|
|
|
|
|
0
|
my $f; |
|
453
|
0
|
|
|
|
|
0
|
my $dir = "PDLA/Transform/Cartography/earth_"; |
|
454
|
0
|
0
|
|
|
|
0
|
$f = ($nd =~ m/^n/i) ? "${dir}night.jpg" : "${dir}day.jpg"; |
|
455
|
|
|
|
|
|
|
|
|
456
|
0
|
|
|
|
|
0
|
local $_; |
|
457
|
0
|
|
|
|
|
0
|
my $im; |
|
458
|
0
|
|
|
|
|
0
|
my $found = 0; |
|
459
|
0
|
|
|
|
|
0
|
foreach(@INC) { |
|
460
|
0
|
|
|
|
|
0
|
my $file = "$_/$f"; |
|
461
|
0
|
0
|
|
|
|
0
|
if(-e $file) { |
|
462
|
0
|
|
|
|
|
0
|
$found = 1; |
|
463
|
0
|
|
|
|
|
0
|
$im = rpic($file)->mv(0,-1); |
|
464
|
|
|
|
|
|
|
} |
|
465
|
0
|
0
|
|
|
|
0
|
last if defined($im); |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
|
|
468
|
0
|
0
|
|
|
|
0
|
barf("earth_image: $f not found in \@INC\n") |
|
469
|
|
|
|
|
|
|
unless defined($found); |
|
470
|
0
|
0
|
|
|
|
0
|
barf("earth_image: couldn't load $f; you may need to install netpbm.\n") |
|
471
|
|
|
|
|
|
|
unless defined($im); |
|
472
|
|
|
|
|
|
|
|
|
473
|
0
|
|
|
|
|
0
|
my $h = $im->fhdr; |
|
474
|
|
|
|
|
|
|
|
|
475
|
0
|
|
|
|
|
0
|
$h->{SIMPLE} = 'T'; |
|
476
|
0
|
|
|
|
|
0
|
$h->{NAXIS} = 3; |
|
477
|
0
|
|
|
|
|
0
|
$h->{NAXIS1}=2048; $h->{CRPIX1}=1024.5; $h->{CRVAL1}=0; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
478
|
0
|
|
|
|
|
0
|
$h->{NAXIS2}=1024; $h->{CRPIX2}=512.5; $h->{CRVAL2}=0; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
479
|
0
|
|
|
|
|
0
|
$h->{NAXIS3}=3, $h->{CRPIX3}=1; $h->{CRVAL3}=0; |
|
|
0
|
|
|
|
|
0
|
|
|
480
|
0
|
|
|
|
|
0
|
$h->{CTYPE1}='Longitude'; $h->{CUNIT1}='degrees'; $h->{CDELT1}=180/1024.0; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
481
|
0
|
|
|
|
|
0
|
$h->{CTYPE2}='Latitude'; $h->{CUNIT2}='degrees'; $h->{CDELT2}=180/1024.0; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
482
|
0
|
|
|
|
|
0
|
$h->{CTYPE3}='RGB'; $h->{CUNIT3}='index'; $h->{CDELT3}=1.0; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
483
|
0
|
|
|
|
|
0
|
$h->{COMMENT}='Plate Caree Projection'; |
|
484
|
0
|
|
|
|
|
0
|
$h->{HISTORY}='PDLA Distribution Image, derived from NASA/MODIS data', |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
$im->hdrcpy(1); |
|
487
|
0
|
|
|
|
|
0
|
$im; |
|
488
|
|
|
|
|
|
|
} |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=head2 clean_lines |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=for usage |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
$x = clean_lines(t_mercator->apply(scalar(earth_coast()))); |
|
495
|
|
|
|
|
|
|
$x = clean_lines($line_pen, [threshold]); |
|
496
|
|
|
|
|
|
|
$x = $lines->clean_lines; |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=for ref |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
(Cartography) PDLA method - remove projection irregularities |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
C massages vector data to remove jumps due to singularities |
|
503
|
|
|
|
|
|
|
in the transform. |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
In the first (scalar) form, C<$line_pen> contains both (X,Y) points and pen |
|
506
|
|
|
|
|
|
|
values suitable to be fed to |
|
507
|
|
|
|
|
|
|
L: |
|
508
|
|
|
|
|
|
|
in the second (list) form, C<$lines> contains the (X,Y) points and C<$pen> |
|
509
|
|
|
|
|
|
|
contains the pen values. |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
C assumes that all the outline polylines are local -- |
|
512
|
|
|
|
|
|
|
that is to say, there are no large jumps. Any jumps larger than a |
|
513
|
|
|
|
|
|
|
threshold size are broken by setting the appropriate pen values to 0. |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
The C parameter sets the relative size of the largest jump, relative |
|
516
|
|
|
|
|
|
|
to the map range (as determined by a min/max operation). The default size is |
|
517
|
|
|
|
|
|
|
0.1. |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
NOTES |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
This almost never catches stuff near the apex of cylindrical maps, |
|
522
|
|
|
|
|
|
|
because the anomalous vectors get arbitrarily small. This could be |
|
523
|
|
|
|
|
|
|
improved somewhat by looking at individual runs of the pen and using |
|
524
|
|
|
|
|
|
|
a relative length scale that is calibrated to the rest of each run. |
|
525
|
|
|
|
|
|
|
it is probably not worth the computational overhead. |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=cut |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
*PDLA::clean_lines = \&clean_lines; |
|
530
|
|
|
|
|
|
|
sub clean_lines { |
|
531
|
0
|
|
|
0
|
1
|
0
|
my($lines) = shift; |
|
532
|
0
|
|
|
|
|
0
|
my($x) = shift; |
|
533
|
0
|
|
|
|
|
0
|
my($y) = shift; |
|
534
|
0
|
|
|
|
|
0
|
my($l,$p,$th); |
|
535
|
|
|
|
|
|
|
|
|
536
|
0
|
|
|
|
|
0
|
$th = 0.1; |
|
537
|
|
|
|
|
|
|
|
|
538
|
0
|
0
|
|
|
|
0
|
if(defined($y)) { |
|
539
|
|
|
|
|
|
|
# separate case with thresh |
|
540
|
0
|
|
|
|
|
0
|
$l = $lines; |
|
541
|
0
|
0
|
|
|
|
0
|
$p = $x->is_inplace?$x:$x->copy; |
|
542
|
0
|
|
|
|
|
0
|
$th = $y; |
|
543
|
|
|
|
|
|
|
} else { |
|
544
|
0
|
0
|
0
|
|
|
0
|
if(!defined($x)) { |
|
|
|
0
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
# duplex case no thresh |
|
546
|
0
|
|
|
|
|
0
|
$l = $lines->(0:1); |
|
547
|
0
|
0
|
|
|
|
0
|
$p = $lines->is_inplace ? $lines->((2)) : $lines->((2))->sever; |
|
548
|
|
|
|
|
|
|
} elsif(UNIVERSAL::isa($x,'PDLA') && |
|
549
|
|
|
|
|
|
|
$lines->((0))->nelem == $x->nelem) { |
|
550
|
|
|
|
|
|
|
# Separate case no thresh |
|
551
|
0
|
|
|
|
|
0
|
$l = $lines; |
|
552
|
0
|
0
|
|
|
|
0
|
$p = $x->is_inplace ? $x : $x->copy;; |
|
553
|
|
|
|
|
|
|
} else { |
|
554
|
|
|
|
|
|
|
# duplex case with thresh |
|
555
|
0
|
|
|
|
|
0
|
$l = $lines->(0:1); |
|
556
|
0
|
0
|
|
|
|
0
|
$p = $lines->is_inplace ? $lines->((2)) : $lines->((2))->sever; |
|
557
|
0
|
|
|
|
|
0
|
$th = $x; |
|
558
|
|
|
|
|
|
|
} |
|
559
|
|
|
|
|
|
|
} |
|
560
|
|
|
|
|
|
|
|
|
561
|
0
|
|
|
|
|
0
|
my $pok = (($p != 0) & isfinite($p)); |
|
562
|
|
|
|
|
|
|
# Kludge to work around minmax bug (nans confuse it!) |
|
563
|
0
|
|
|
|
|
0
|
my($l0) = $l->((0)); |
|
564
|
0
|
|
|
|
|
0
|
my($x0,$x1) = $l0->where(isfinite($l0) & $pok)->minmax; |
|
565
|
0
|
|
|
|
|
0
|
my($xth) = abs($x1-$x0) * $th; |
|
566
|
|
|
|
|
|
|
|
|
567
|
0
|
|
|
|
|
0
|
my($l1) = $l->((1)); |
|
568
|
0
|
|
|
|
|
0
|
($x0,$x1) = $l1->where(isfinite($l1) & $pok)->minmax; |
|
569
|
0
|
|
|
|
|
0
|
my($yth) = abs($x1-$x0) * $th; |
|
570
|
|
|
|
|
|
|
|
|
571
|
0
|
|
|
|
|
0
|
my $diff = abs($l->(:,1:-1) - $l->(:,0:-2)); |
|
572
|
|
|
|
|
|
|
|
|
573
|
0
|
|
|
|
|
0
|
$diff->where(!isfinite($diff)) .= 2*($xth + $yth); |
|
574
|
0
|
|
|
|
|
0
|
$p->where(($diff->((0)) > $xth) | ($diff->((1)) > $yth)) .= 0; |
|
575
|
0
|
0
|
|
|
|
0
|
if(wantarray){ |
|
576
|
0
|
|
|
|
|
0
|
return($l,$p); |
|
577
|
|
|
|
|
|
|
} else { |
|
578
|
0
|
|
|
|
|
0
|
return $l->append($p->dummy(0,1)); |
|
579
|
|
|
|
|
|
|
} |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
###################################################################### |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
### |
|
587
|
|
|
|
|
|
|
# Units parser |
|
588
|
|
|
|
|
|
|
# Get unit, return conversion factor to radii, or undef if no match found. |
|
589
|
|
|
|
|
|
|
# |
|
590
|
|
|
|
|
|
|
sub _uconv{ |
|
591
|
|
|
|
|
|
|
### |
|
592
|
|
|
|
|
|
|
# Replace this with a more general units resolver call! |
|
593
|
|
|
|
|
|
|
### |
|
594
|
3
|
|
|
3
|
|
8
|
local($_) = shift; |
|
595
|
3
|
|
|
|
|
5
|
my($silent) =shift; |
|
596
|
|
|
|
|
|
|
|
|
597
|
3
|
0
|
|
|
|
44
|
my($x) = |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
( m/^deg/i ? $DEG2RAD : |
|
599
|
|
|
|
|
|
|
m/^arcmin/i ? $DEG2RAD / 60 : |
|
600
|
|
|
|
|
|
|
m/^arcsec/i ? $DEG2RAD / 3600 : |
|
601
|
|
|
|
|
|
|
m/^hour/i ? $DEG2RAD * 15 : # Right ascension |
|
602
|
|
|
|
|
|
|
m/^min/i ? $DEG2RAD * 15/60 : # Right ascension |
|
603
|
|
|
|
|
|
|
m/^microrad/i ? 1e-6 : |
|
604
|
|
|
|
|
|
|
m/^millirad/i ? 1e-3 : |
|
605
|
|
|
|
|
|
|
m/^rad(ian)?s?$/i ? 1.0 : |
|
606
|
|
|
|
|
|
|
m/^meter/ ? 1.0/6371000 : # Assuming Earth cartography! |
|
607
|
|
|
|
|
|
|
m/^kilometer/ ? 1.0/6371 : |
|
608
|
|
|
|
|
|
|
m/^km/ ? 1.0/6371 : |
|
609
|
|
|
|
|
|
|
m/^Mm/ ? 1.0/6.371 : |
|
610
|
|
|
|
|
|
|
m/^mile/ ? 1.0/(637100000/2.54/12/5280) : |
|
611
|
|
|
|
|
|
|
undef |
|
612
|
|
|
|
|
|
|
); |
|
613
|
3
|
0
|
33
|
|
|
12
|
print STDERR "Cartography: unrecognized unit '$_'\n" |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
614
|
|
|
|
|
|
|
if( (!defined $x) && !$silent && ($PDLA::debug || $PDLA::verbose)); |
|
615
|
3
|
|
|
|
|
22
|
$x; |
|
616
|
|
|
|
|
|
|
} |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
### |
|
619
|
|
|
|
|
|
|
# |
|
620
|
|
|
|
|
|
|
# Cartography general constructor -- called by the individual map |
|
621
|
|
|
|
|
|
|
# constructors. Not underscored because it's certainly OK to call from |
|
622
|
|
|
|
|
|
|
# outside -- but the *last* argument is the name of the transform. |
|
623
|
|
|
|
|
|
|
# |
|
624
|
|
|
|
|
|
|
# The options list is put into the {options} field of the newly constructed |
|
625
|
|
|
|
|
|
|
# Transform -- fastidious subclass constructors will want to delete it before |
|
626
|
|
|
|
|
|
|
# returning. |
|
627
|
|
|
|
|
|
|
# |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
|
|
630
|
2
|
|
|
2
|
|
9
|
sub _new { new('PDLA::Transform::Cartography',@_); } # not exported |
|
631
|
|
|
|
|
|
|
sub new { |
|
632
|
2
|
|
|
2
|
0
|
6
|
my($class) = shift; |
|
633
|
2
|
|
|
|
|
5
|
my($name) = pop; |
|
634
|
|
|
|
|
|
|
|
|
635
|
2
|
|
|
|
|
4
|
my($o) = $_[0]; |
|
636
|
2
|
50
|
|
|
|
13
|
$o = {@_} |
|
637
|
|
|
|
|
|
|
unless(ref $o eq 'HASH'); |
|
638
|
|
|
|
|
|
|
|
|
639
|
2
|
|
|
|
|
8
|
my($me) = PDLA::Transform::new($class); |
|
640
|
2
|
|
|
|
|
101
|
$me->{idim} = $me->{odim} = 2; |
|
641
|
2
|
|
|
|
|
6
|
$me->{name} = $name; |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
#### |
|
644
|
|
|
|
|
|
|
# Parse origin and units arguments |
|
645
|
|
|
|
|
|
|
# |
|
646
|
2
|
|
|
|
|
10
|
my $or = _opt($o,['o','origin','Origin'],zeroes(2)); |
|
647
|
2
|
50
|
|
|
|
30
|
if($or->nelem != 2) { |
|
648
|
0
|
|
|
|
|
0
|
croak("PDLA::Transform::Cartography: origin must have 2 elements\n"); |
|
649
|
|
|
|
|
|
|
} |
|
650
|
|
|
|
|
|
|
|
|
651
|
2
|
|
|
|
|
9
|
my($l) = _opt($o,['l','L']); |
|
652
|
2
|
|
|
|
|
8
|
my($b_angle) = _opt($o,['b','B']); |
|
653
|
|
|
|
|
|
|
|
|
654
|
2
|
50
|
|
|
|
6
|
$or->(0) .= $l if defined($l); |
|
655
|
2
|
50
|
|
|
|
5
|
$or->(1) .= $b_angle if defined($b_angle); |
|
656
|
|
|
|
|
|
|
|
|
657
|
2
|
|
|
|
|
7
|
my $roll = topdl(_opt($o,['r','roll','Roll','P'],0)); |
|
658
|
2
|
|
|
|
|
96
|
my $unit = _opt($o,['u','unit','Unit'],'degrees'); |
|
659
|
|
|
|
|
|
|
|
|
660
|
2
|
|
|
|
|
7
|
$me->{params}->{conv} = my $conv = _uconv($unit); |
|
661
|
2
|
|
|
|
|
6
|
$me->{params}->{u} = $unit; |
|
662
|
|
|
|
|
|
|
|
|
663
|
2
|
|
|
|
|
6
|
$me->{itype} = ['longitude','latitude']; |
|
664
|
2
|
|
|
|
|
8
|
$me->{iunit} = [$me->{params}->{u},$me->{params}->{u}]; |
|
665
|
|
|
|
|
|
|
|
|
666
|
2
|
|
|
|
|
9
|
my($ou) = _opt($o,['ou','ounit','OutputUnit'],undef); |
|
667
|
2
|
|
|
|
|
6
|
$me->{params}->{ou} = $ou; |
|
668
|
2
|
50
|
|
|
|
5
|
if(defined $ou) { |
|
669
|
0
|
0
|
|
|
|
0
|
if(!(ref $ou)) { |
|
670
|
0
|
|
|
|
|
0
|
$me->{params}->{oconv} = _uconv($ou); |
|
671
|
|
|
|
|
|
|
} else { |
|
672
|
0
|
|
|
|
|
0
|
my @oconv; |
|
673
|
0
|
|
|
|
|
0
|
map {push(@oconv,_uconv($_))} @$ou; |
|
|
0
|
|
|
|
|
0
|
|
|
674
|
0
|
|
|
|
|
0
|
$me->{params}->{oconv} = topdl([@oconv]); |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
} else { |
|
677
|
2
|
|
|
|
|
5
|
$me->{params}->{oconv} = undef; |
|
678
|
|
|
|
|
|
|
} |
|
679
|
2
|
|
|
|
|
5
|
$me->{ounit} = $me->{params}->{ou}; |
|
680
|
|
|
|
|
|
|
|
|
681
|
2
|
|
|
|
|
24
|
$me->{params}->{o} = $or * $conv; |
|
682
|
2
|
|
|
|
|
19
|
$me->{params}->{roll} = $roll * $conv; |
|
683
|
|
|
|
|
|
|
|
|
684
|
2
|
|
|
|
|
12
|
$me->{params}->{bad} = _opt($o,['b','bad','Bad','missing','Missing'], |
|
685
|
|
|
|
|
|
|
asin(pdl(1.1))); |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
# Get the standard parallel (in general there's only one; the conics |
|
688
|
|
|
|
|
|
|
# have two but that's handled by _c_new) |
|
689
|
|
|
|
|
|
|
$me->{params}->{std} = topdl(_opt($me->{options}, |
|
690
|
|
|
|
|
|
|
['s','std','standard','Standard'], |
|
691
|
2
|
|
|
|
|
13
|
0))->at(0) * $me->{params}->{conv}; |
|
692
|
|
|
|
|
|
|
|
|
693
|
2
|
|
|
|
|
104
|
$me->{options} = $o; |
|
694
|
2
|
|
|
|
|
9
|
$me; |
|
695
|
|
|
|
|
|
|
} |
|
696
|
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
# Compose self with t_rot_sphere if necessary -- useful for |
|
698
|
|
|
|
|
|
|
# finishing off the transformations that accept the origin and roll |
|
699
|
|
|
|
|
|
|
# options. |
|
700
|
|
|
|
|
|
|
sub PDLA::Transform::Cartography::_finish { |
|
701
|
0
|
|
|
0
|
|
0
|
my($me) = shift; |
|
702
|
0
|
0
|
0
|
|
|
0
|
if( ($me->{params}->{o}->(0) != 0) || |
|
|
|
|
0
|
|
|
|
|
|
703
|
|
|
|
|
|
|
($me->{params}->{o}->(1) != 0) || |
|
704
|
|
|
|
|
|
|
($me->{params}->{roll} != 0) |
|
705
|
|
|
|
|
|
|
) { |
|
706
|
0
|
|
|
|
|
0
|
my $out = t_compose($me,t_rot_sphere($me->{options})); |
|
707
|
0
|
|
|
|
|
0
|
$out->{itype} = $me->{itype}; |
|
708
|
0
|
|
|
|
|
0
|
$out->{iunit} = $me->{iunit}; |
|
709
|
0
|
|
|
|
|
0
|
$out->{otype} = $me->{otype}; |
|
710
|
0
|
|
|
|
|
0
|
$out->{ounit} = $me->{ounit}; |
|
711
|
0
|
|
|
|
|
0
|
$out->{odim} = 2; |
|
712
|
0
|
|
|
|
|
0
|
$out->{idim} = 2; |
|
713
|
0
|
|
|
|
|
0
|
return $out; |
|
714
|
|
|
|
|
|
|
} |
|
715
|
0
|
|
|
|
|
0
|
return $me; |
|
716
|
|
|
|
|
|
|
} |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
###################################################################### |
|
720
|
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=head2 t_unit_sphere |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=for usage |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
$t = t_unit_sphere(); |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=for ref |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
(Cartography) 3-D globe projection (conformal; authalic) |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
This is similar to the inverse of |
|
732
|
|
|
|
|
|
|
L, |
|
733
|
|
|
|
|
|
|
but the |
|
734
|
|
|
|
|
|
|
inverse transform projects 3-D coordinates onto the unit sphere, |
|
735
|
|
|
|
|
|
|
yielding only a 2-D (lon/lat) output. Similarly, the forward |
|
736
|
|
|
|
|
|
|
transform deprojects 2-D (lon/lat) coordinates onto the surface of a |
|
737
|
|
|
|
|
|
|
unit sphere. |
|
738
|
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
The cartesian system has its Z axis pointing through the pole of the |
|
740
|
|
|
|
|
|
|
(lon,lat) system, and its X axis pointing through the equator at the |
|
741
|
|
|
|
|
|
|
prime meridian. |
|
742
|
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
Unit sphere mapping is unusual in that it is both conformal and authalic. |
|
744
|
|
|
|
|
|
|
That is possible because it properly embeds the sphere in 3-space, as a |
|
745
|
|
|
|
|
|
|
notional globe. |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
This is handy as an intermediate step in lots of transforms, as |
|
748
|
|
|
|
|
|
|
Cartesian 3-space is cleaner to work with than spherical 2-space. |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
Higher dimensional indices are preserved, so that "rider" indices (such as |
|
751
|
|
|
|
|
|
|
pen value) are propagated. |
|
752
|
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
There is no oblique transform for t_unit_sphere, largely because |
|
754
|
|
|
|
|
|
|
it's so easy to rotate the output using t_linear once it's out into |
|
755
|
|
|
|
|
|
|
Cartesian space. In fact, the other projections implement oblique |
|
756
|
|
|
|
|
|
|
transforms by |
|
757
|
|
|
|
|
|
|
L |
|
758
|
|
|
|
|
|
|
L with |
|
759
|
|
|
|
|
|
|
L. |
|
760
|
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
OPTIONS: |
|
762
|
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=over 3 |
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
=item radius, Radius (default 1.0) |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
The radius of the sphere, for the inverse transform. (Radius is ignored |
|
768
|
|
|
|
|
|
|
in the forward transform). Defaults to 1.0 so that the resulting Cartesian |
|
769
|
|
|
|
|
|
|
coordinates are in units of "body radii". |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
=back |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=cut |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
sub t_unit_sphere { |
|
776
|
1
|
|
|
1
|
1
|
6
|
my($me) = _new(@_,'Unit Sphere Projection'); |
|
777
|
1
|
|
|
|
|
4
|
$me->{odim} = 3; |
|
778
|
|
|
|
|
|
|
|
|
779
|
1
|
|
|
|
|
3
|
$me->{params}->{otype} = ['X','Y','Z']; |
|
780
|
1
|
|
|
|
|
3
|
$me->{params}->{ounit} = ['body radii','body radii','body radii']; |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
$me->{params}->{r} = topdl(_opt($me->{options}, |
|
783
|
1
|
|
|
|
|
5
|
['r','radius','Radius'], |
|
784
|
|
|
|
|
|
|
1.0) |
|
785
|
|
|
|
|
|
|
)->at(0); |
|
786
|
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
$me->{func} = sub { |
|
789
|
8
|
|
|
8
|
|
23
|
my($d,$o) = @_; |
|
790
|
8
|
|
|
|
|
40
|
my(@dims) = $d->dims; |
|
791
|
8
|
|
|
|
|
274
|
$dims[0] ++; |
|
792
|
8
|
|
|
|
|
55
|
my $out = zeroes(@dims); |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
my($thetaphi) = ((defined $o->{conv} && $o->{conv} != 1.0) ? |
|
795
|
8
|
50
|
33
|
|
|
5489
|
$d->(0:1) * $o->{conv} : $d->(0:1) |
|
796
|
|
|
|
|
|
|
); |
|
797
|
|
|
|
|
|
|
|
|
798
|
8
|
|
|
|
|
183
|
my $th = $thetaphi->((0)); |
|
799
|
8
|
|
|
|
|
106
|
my $ph = $thetaphi->((1)); |
|
800
|
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
# use x as a holding tank for the cos-phi multiplier |
|
802
|
8
|
|
|
|
|
94
|
$out->((0)) .= $o->{r} * cos($ph) ; |
|
803
|
8
|
|
|
|
|
40775
|
$out->((1)) .= $out->((0)) * sin($th); |
|
804
|
8
|
|
|
|
|
44049
|
$out->((0)) *= cos($th); |
|
805
|
|
|
|
|
|
|
|
|
806
|
8
|
|
|
|
|
40081
|
$out->((2)) .= $o->{r} * sin($ph); |
|
807
|
|
|
|
|
|
|
|
|
808
|
8
|
50
|
|
|
|
40402
|
if($d->dim(0) > 2) { |
|
809
|
0
|
|
|
|
|
0
|
$out->(3:-1) .= $d->(2:-1); |
|
810
|
|
|
|
|
|
|
} |
|
811
|
|
|
|
|
|
|
|
|
812
|
8
|
|
|
|
|
106
|
$out; |
|
813
|
|
|
|
|
|
|
|
|
814
|
1
|
|
|
|
|
49
|
}; |
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
817
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
818
|
|
|
|
|
|
|
|
|
819
|
0
|
|
|
|
|
0
|
my($d0,$d1,$d2) = ($d->((0)),$d->((1)),$d->((2))); |
|
820
|
0
|
|
|
|
|
0
|
my($r) = sqrt(($d->(0:2)*$d->(0:2))->sumover); |
|
821
|
0
|
|
|
|
|
0
|
my(@dims) = $d->dims; |
|
822
|
0
|
|
|
|
|
0
|
$dims[0]--; |
|
823
|
0
|
|
|
|
|
0
|
my($out) = zeroes(@dims); |
|
824
|
|
|
|
|
|
|
|
|
825
|
0
|
|
|
|
|
0
|
$out->((0)) .= atan2($d1,$d0); |
|
826
|
0
|
|
|
|
|
0
|
$out->((1)) .= asin($d2/$r); |
|
827
|
|
|
|
|
|
|
|
|
828
|
0
|
0
|
|
|
|
0
|
if($d->dim(0) > 3) { |
|
829
|
0
|
|
|
|
|
0
|
$out->(2:-1) .= $d->(3:-1); |
|
830
|
|
|
|
|
|
|
} |
|
831
|
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
$out->(0:1) /= $o->{conv} |
|
833
|
0
|
0
|
0
|
|
|
0
|
if(defined $o->{conv} && $o->{conv} != 1.0); |
|
834
|
|
|
|
|
|
|
|
|
835
|
0
|
|
|
|
|
0
|
$out; |
|
836
|
1
|
|
|
|
|
5
|
}; |
|
837
|
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
|
|
839
|
1
|
|
|
|
|
5
|
$me; |
|
840
|
|
|
|
|
|
|
} |
|
841
|
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
###################################################################### |
|
843
|
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
=head2 t_rot_sphere |
|
845
|
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=for usage |
|
847
|
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
$t = t_rot_sphere({origin=>[,],roll=>[]}); |
|
849
|
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=for ref |
|
851
|
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
(Cartography) Generate oblique projections |
|
853
|
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
You feed in the origin in (theta,phi) and a roll angle, and you get back |
|
855
|
|
|
|
|
|
|
out (theta', phi') coordinates. This is useful for making oblique or |
|
856
|
|
|
|
|
|
|
transverse projections: just compose t_rot_sphere with your favorite |
|
857
|
|
|
|
|
|
|
projection and you get an oblique one. |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
Most of the projections automagically compose themselves with t_rot_sphere |
|
860
|
|
|
|
|
|
|
if you feed in an origin or roll angle. |
|
861
|
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
t_rot_sphere converts the base plate caree projection (straight lon, straight |
|
863
|
|
|
|
|
|
|
lat) to a Cassini projection. |
|
864
|
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
OPTIONS |
|
866
|
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
=over 3 |
|
868
|
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
870
|
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=back |
|
872
|
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
=cut |
|
874
|
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
# helper routine for making the rotation matrix |
|
876
|
|
|
|
|
|
|
sub _rotmat { |
|
877
|
2
|
|
|
2
|
|
47
|
my($th,$ph,$r) = @_; |
|
878
|
|
|
|
|
|
|
|
|
879
|
2
|
|
|
|
|
15
|
pdl( [ cos($th) , -sin($th), 0 ], # apply theta |
|
880
|
|
|
|
|
|
|
[ sin($th) , cos($th), 0 ], |
|
881
|
|
|
|
|
|
|
[ 0, 0, 1 ] ) |
|
882
|
|
|
|
|
|
|
x |
|
883
|
|
|
|
|
|
|
pdl( [ cos($ph), 0, -sin($ph) ], # apply phi |
|
884
|
|
|
|
|
|
|
[ 0, 1, 0 ], |
|
885
|
|
|
|
|
|
|
[ sin($ph), 0, cos($ph) ] ) |
|
886
|
|
|
|
|
|
|
x |
|
887
|
|
|
|
|
|
|
pdl( [ 1, 0 , 0 ], # apply roll last |
|
888
|
|
|
|
|
|
|
[ 0, cos($r), -sin($r) ], |
|
889
|
|
|
|
|
|
|
[ 0, sin($r), cos($r) ]) |
|
890
|
|
|
|
|
|
|
; |
|
891
|
|
|
|
|
|
|
} |
|
892
|
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
sub t_rot_sphere { |
|
894
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,'Spherical rotation'); |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
|
|
897
|
0
|
|
|
|
|
0
|
my($th,$ph) = $me->{params}->{o}->list; |
|
898
|
0
|
|
|
|
|
0
|
my($r) = $me->{params}->{roll}->at(0); |
|
899
|
|
|
|
|
|
|
|
|
900
|
0
|
|
|
|
|
0
|
my($rotmat) = _rotmat($th,$ph,$r); |
|
901
|
|
|
|
|
|
|
|
|
902
|
0
|
|
|
|
|
0
|
my $out = t_wrap( t_linear(m=>$rotmat, d=>3), t_unit_sphere()); |
|
903
|
0
|
|
|
|
|
0
|
$out->{itype} = $me->{itype}; |
|
904
|
0
|
|
|
|
|
0
|
$out->{iunit} = $me->{iunit}; |
|
905
|
0
|
|
|
|
|
0
|
$out->{otype} = ['rotated longitude','rotated latitude']; |
|
906
|
0
|
|
|
|
|
0
|
$out->{ounit} = $me->{iunit}; |
|
907
|
|
|
|
|
|
|
|
|
908
|
0
|
|
|
|
|
0
|
$out; |
|
909
|
|
|
|
|
|
|
} |
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
###################################################################### |
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
=head2 t_orthographic |
|
915
|
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
=for usage |
|
917
|
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
$t = t_orthographic(); |
|
919
|
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
=for ref |
|
921
|
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
(Cartography) Ortho. projection (azimuthal; perspective) |
|
923
|
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
This is a perspective view as seen from infinite distance. You |
|
925
|
|
|
|
|
|
|
can specify the sub-viewer point in (lon,lat) coordinates, and a rotation |
|
926
|
|
|
|
|
|
|
angle of the map CW from (north=up). This is equivalent to specify |
|
927
|
|
|
|
|
|
|
viewer roll angle CCW from (north=up). |
|
928
|
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
t_orthographic is a convenience interface to t_unit_sphere -- it is implemented |
|
930
|
|
|
|
|
|
|
as a composition of a t_unit_sphere call, a rotation, and a slice. |
|
931
|
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
[*] In the default case where the near hemisphere is mapped, the |
|
933
|
|
|
|
|
|
|
inverse exists. There is no single inverse for the whole-sphere case, |
|
934
|
|
|
|
|
|
|
so the inverse transform superimposes everything on a single |
|
935
|
|
|
|
|
|
|
hemisphere. If you want an invertible 3-D transform, you want |
|
936
|
|
|
|
|
|
|
L. |
|
937
|
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
OPTIONS |
|
939
|
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
=over 3 |
|
941
|
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
943
|
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
=item m, mask, Mask, h, hemisphere, Hemisphere [default 'near'] |
|
945
|
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
The hemisphere to keep in the projection (see L). |
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
=back |
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
NOTES |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
Alone of the various projections, this one does not use |
|
953
|
|
|
|
|
|
|
L to handle the standard options, because |
|
954
|
|
|
|
|
|
|
the cartesian coordinates of the rotated sphere are already correctly |
|
955
|
|
|
|
|
|
|
projected -- t_rot_sphere would put them back into (theta', phi') |
|
956
|
|
|
|
|
|
|
coordinates. |
|
957
|
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=cut |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
sub t_orthographic { |
|
961
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,'Orthographic Projection'); |
|
962
|
|
|
|
|
|
|
|
|
963
|
0
|
|
|
|
|
0
|
$me->{otype} = ['projected X','projected Y']; |
|
964
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['body radii','body radii']; |
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
my $m= _opt($me->{options}, |
|
967
|
0
|
|
|
|
|
0
|
['m','mask','Mask','h','hemi','hemisphere','Hemisphere'], |
|
968
|
|
|
|
|
|
|
1); |
|
969
|
0
|
0
|
|
|
|
0
|
if($m=~m/^b/i) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
970
|
0
|
|
|
|
|
0
|
$me->{params}->{m} = 0; |
|
971
|
|
|
|
|
|
|
} elsif($m=~m/^n/i) { |
|
972
|
0
|
|
|
|
|
0
|
$me->{params}->{m} = 1; |
|
973
|
|
|
|
|
|
|
} elsif($m=~m/^f/i) { |
|
974
|
0
|
|
|
|
|
0
|
$me->{params}->{m} = 2; |
|
975
|
|
|
|
|
|
|
} else { |
|
976
|
0
|
|
|
|
|
0
|
$me->{params}->{m} = $m; |
|
977
|
|
|
|
|
|
|
} |
|
978
|
|
|
|
|
|
|
|
|
979
|
0
|
|
|
|
|
0
|
my $origin= $me->{params}->{o} * $RAD2DEG; |
|
980
|
0
|
|
|
|
|
0
|
my $roll = $me->{params}->{roll} * $RAD2DEG; |
|
981
|
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
$me->{params}->{t_int} = |
|
983
|
|
|
|
|
|
|
t_compose( |
|
984
|
|
|
|
|
|
|
t_linear(rot=>[90 - $origin->at(1), |
|
985
|
|
|
|
|
|
|
0, |
|
986
|
|
|
|
|
|
|
90 + $origin->at(0)], |
|
987
|
|
|
|
|
|
|
d=>3), |
|
988
|
|
|
|
|
|
|
t_unit_sphere(u=>$me->{params}->{u}) |
|
989
|
0
|
|
|
|
|
0
|
); |
|
990
|
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
$me->{params}->{t_int} = |
|
992
|
|
|
|
|
|
|
t_compose( |
|
993
|
|
|
|
|
|
|
t_linear(rot=>[0,0,$roll->at(0)],d=>3), |
|
994
|
|
|
|
|
|
|
$me->{params}->{t_int} |
|
995
|
|
|
|
|
|
|
) |
|
996
|
0
|
0
|
|
|
|
0
|
if($roll->at(0)); |
|
997
|
|
|
|
|
|
|
|
|
998
|
0
|
|
|
|
|
0
|
$me->{name} = "orthographic"; |
|
999
|
|
|
|
|
|
|
|
|
1000
|
0
|
|
|
|
|
0
|
$me->{idim} = 2; |
|
1001
|
0
|
|
|
|
|
0
|
$me->{odim} = 2; |
|
1002
|
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1004
|
0
|
|
|
0
|
|
0
|
my ($d,$o) = @_ ; |
|
1005
|
0
|
|
|
|
|
0
|
my ($out) = $o->{t_int}->apply($d); |
|
1006
|
0
|
0
|
|
|
|
0
|
if($o->{m}) { |
|
1007
|
0
|
|
|
|
|
0
|
my $idx; |
|
1008
|
|
|
|
|
|
|
$idx = whichND($out->((2)) < 0) |
|
1009
|
0
|
0
|
|
|
|
0
|
if($o->{m} == 1); |
|
1010
|
|
|
|
|
|
|
$idx = whichND($out->((2)) > 0) |
|
1011
|
0
|
0
|
|
|
|
0
|
if($o->{m} == 2); |
|
1012
|
0
|
0
|
0
|
|
|
0
|
if(defined $idx && ref $idx eq 'PDLA' && $idx->nelem){ |
|
|
|
|
0
|
|
|
|
|
|
1013
|
0
|
|
|
|
|
0
|
$out->((0))->range($idx) .= $o->{bad}; |
|
1014
|
0
|
|
|
|
|
0
|
$out->((1))->range($idx) .= $o->{bad}; |
|
1015
|
|
|
|
|
|
|
} |
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
0
|
|
|
|
|
0
|
my($d0) = $out->dim(0); |
|
1019
|
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
# Remove the Z direction |
|
1021
|
0
|
0
|
|
|
|
0
|
($d0 > 3) ? $out->(pdl(0,1,3..$d0-1)) : $out->(0:1); |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
0
|
|
|
|
|
0
|
}; |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
# This is slow to run, quick to code -- could be made better by |
|
1026
|
|
|
|
|
|
|
# having its own 2-d inverse instead of calling the internal one. |
|
1027
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1028
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1029
|
0
|
|
|
|
|
0
|
my($d1) = $d->(0:1); |
|
1030
|
0
|
|
|
|
|
0
|
my(@dims) = $d->dims; |
|
1031
|
0
|
|
|
|
|
0
|
$dims[0]++; |
|
1032
|
0
|
|
|
|
|
0
|
my($out) = zeroes(@dims); |
|
1033
|
0
|
|
|
|
|
0
|
$out->(0:1) .= $d1; |
|
1034
|
0
|
0
|
|
|
|
0
|
$out->(3:-1) .= $d->(2:-1) |
|
1035
|
|
|
|
|
|
|
if($dims[0] > 3); |
|
1036
|
|
|
|
|
|
|
|
|
1037
|
0
|
|
|
|
|
0
|
$out->((2)) .= sqrt(1 - ($d1*$d1)->sumover); |
|
1038
|
0
|
0
|
|
|
|
0
|
$out->((2)) *= -1 if($o->{m} == 2); |
|
1039
|
|
|
|
|
|
|
|
|
1040
|
0
|
|
|
|
|
0
|
$o->{t_int}->invert($out); |
|
1041
|
0
|
|
|
|
|
0
|
}; |
|
1042
|
|
|
|
|
|
|
|
|
1043
|
0
|
|
|
|
|
0
|
$me; |
|
1044
|
|
|
|
|
|
|
} |
|
1045
|
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
###################################################################### |
|
1047
|
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
=head2 t_caree |
|
1049
|
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
=for usage |
|
1051
|
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
$t = t_caree(); |
|
1053
|
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
=for ref |
|
1055
|
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
(Cartography) Plate Caree projection (cylindrical; equidistant) |
|
1057
|
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
This is the simple Plate Caree projection -- also called a "lat/lon plot". |
|
1059
|
|
|
|
|
|
|
The horizontal axis is theta; the vertical axis is phi. This is a no-op |
|
1060
|
|
|
|
|
|
|
if the angular unit is radians; it is a simple scale otherwise. |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
OPTIONS |
|
1063
|
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
=over 3 |
|
1065
|
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
=item s, std, standard, Standard (default 0) |
|
1069
|
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
The standard parallel where the transformation is conformal. Conformality |
|
1071
|
|
|
|
|
|
|
is achieved by shrinking of the horizontal scale to match the |
|
1072
|
|
|
|
|
|
|
vertical scale (which is correct everywhere). |
|
1073
|
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
=back |
|
1075
|
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
=cut |
|
1077
|
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
@PDLA::Transform::Cartography::Caree::ISA = ('PDLA::Transform::Cartography'); |
|
1079
|
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
sub t_caree { |
|
1081
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,'Plate Caree Projection'); |
|
1082
|
0
|
|
|
|
|
0
|
my $p = $me->{params}; |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
0
|
|
|
|
|
0
|
$me->{otype} = ['projected longitude','latitude']; |
|
1085
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['proj. body radii','body radii']; |
|
1086
|
|
|
|
|
|
|
|
|
1087
|
0
|
|
|
|
|
0
|
$p->{stretch} = cos($p->{std}); |
|
1088
|
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1090
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1091
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1092
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $o->{conv}; |
|
1093
|
0
|
|
|
|
|
0
|
$out->(0) *= $p->{stretch}; |
|
1094
|
0
|
|
|
|
|
0
|
$out; |
|
1095
|
0
|
|
|
|
|
0
|
}; |
|
1096
|
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1098
|
0
|
|
|
0
|
|
0
|
my($d,$o)= @_; |
|
1099
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1100
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
1101
|
0
|
|
|
|
|
0
|
$out->(0) /= $p->{stretch}; |
|
1102
|
0
|
|
|
|
|
0
|
$out; |
|
1103
|
0
|
|
|
|
|
0
|
}; |
|
1104
|
|
|
|
|
|
|
|
|
1105
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
1106
|
|
|
|
|
|
|
} |
|
1107
|
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
###################################################################### |
|
1109
|
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
=head2 t_mercator |
|
1111
|
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=for usage |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
$t = t_mercator(); |
|
1115
|
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
=for ref |
|
1117
|
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
(Cartography) Mercator projection (cylindrical; conformal) |
|
1119
|
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
This is perhaps the most famous of all map projections: meridians are mapped |
|
1121
|
|
|
|
|
|
|
to parallel vertical lines and parallels are unevenly spaced horizontal lines. |
|
1122
|
|
|
|
|
|
|
The poles are shifted to +/- infinity. The output values are in units of |
|
1123
|
|
|
|
|
|
|
globe-radii for easy conversion to kilometers; hence the horizontal extent |
|
1124
|
|
|
|
|
|
|
is -pi to pi. |
|
1125
|
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
You can get oblique Mercator projections by specifying the C or |
|
1127
|
|
|
|
|
|
|
C options; this is implemented via L. |
|
1128
|
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
OPTIONS |
|
1130
|
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
=over 3 |
|
1132
|
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1134
|
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
=item c, clip, Clip (default 75 [degrees]) |
|
1136
|
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
The north/south clipping boundary of the transformation. Because the poles are |
|
1138
|
|
|
|
|
|
|
displaced to infinity, many applications require a clipping boundary. The |
|
1139
|
|
|
|
|
|
|
value is in whatever angular unit you set with the standard 'units' option. |
|
1140
|
|
|
|
|
|
|
The default roughly matches interesting landforms on Earth. |
|
1141
|
|
|
|
|
|
|
For no clipping at all, set b=>0. For asymmetric clipping, use a 2-element |
|
1142
|
|
|
|
|
|
|
list ref or piddle. |
|
1143
|
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
=item s, std, Standard (default 0) |
|
1145
|
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
This is the parallel at which the map has correct scale. The scale |
|
1147
|
|
|
|
|
|
|
is also correct at the parallel of opposite sign. |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
=back |
|
1150
|
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=cut |
|
1152
|
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
@PDLA::Transform::Cartography::Mercator::ISA = ('PDLA::Transform::Cartography'); |
|
1155
|
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
sub t_mercator { |
|
1157
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,'Mercator Projection'); |
|
1158
|
0
|
|
|
|
|
0
|
my $p = $me->{params}; |
|
1159
|
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
# This is a lot of shenanigans just to get the clip parallels, but what the |
|
1161
|
|
|
|
|
|
|
# heck -- it's not a hot spot and it saves copying the input data (for |
|
1162
|
|
|
|
|
|
|
# nondestructive clipping). |
|
1163
|
|
|
|
|
|
|
$p->{c} = _opt($me->{options}, |
|
1164
|
0
|
|
|
|
|
0
|
['c','clip','Clip'], |
|
1165
|
|
|
|
|
|
|
undef); |
|
1166
|
0
|
0
|
|
|
|
0
|
if(defined($p->{c})) { |
|
1167
|
0
|
|
|
|
|
0
|
$p->{c} = topdl($p->{c}); |
|
1168
|
0
|
|
|
|
|
0
|
$p->{c} *= $p->{conv}; |
|
1169
|
|
|
|
|
|
|
} else { |
|
1170
|
0
|
|
|
|
|
0
|
$p->{c} = pdl($DEG2RAD * 75); |
|
1171
|
|
|
|
|
|
|
} |
|
1172
|
0
|
0
|
|
|
|
0
|
$p->{c} = abs($p->{c}) * pdl(-1,1) if($p->{c}->nelem == 1); |
|
1173
|
|
|
|
|
|
|
|
|
1174
|
0
|
|
|
|
|
0
|
$p->{c} = log(tan(($p->{c}/2) + $PI/4)); |
|
1175
|
0
|
|
|
|
|
0
|
$p->{c} = [$p->{c}->list]; |
|
1176
|
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
$p->{std} = topdl(_opt($me->{options}, |
|
1178
|
|
|
|
|
|
|
['s','std','standard','Standard'], |
|
1179
|
0
|
|
|
|
|
0
|
0))->at(0) * $p->{conv}; |
|
1180
|
|
|
|
|
|
|
|
|
1181
|
0
|
0
|
|
|
|
0
|
if($p->{std} == 0) { |
|
1182
|
0
|
|
|
|
|
0
|
$me->{otype} = ['longitude','tan latitude']; |
|
1183
|
0
|
0
|
|
|
|
0
|
$me->{ounit} = ['radians',' '] unless(defined $me->{ounit}); |
|
1184
|
|
|
|
|
|
|
} else { |
|
1185
|
0
|
|
|
|
|
0
|
$me->{otype} = ['proj. longitude','proj. tan latitude']; |
|
1186
|
0
|
0
|
|
|
|
0
|
$me->{ounit} = ['radians',' '] unless(defined $me->{ounit}); |
|
1187
|
|
|
|
|
|
|
} |
|
1188
|
|
|
|
|
|
|
|
|
1189
|
0
|
|
|
|
|
0
|
$p->{stretch} = cos($p->{std}); |
|
1190
|
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1192
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1193
|
|
|
|
|
|
|
|
|
1194
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1195
|
|
|
|
|
|
|
|
|
1196
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $o->{conv}; |
|
1197
|
|
|
|
|
|
|
|
|
1198
|
0
|
|
|
|
|
0
|
$out->((1)) .= log(tan($out->((1))/2 + $PI/4)); |
|
1199
|
0
|
|
|
|
|
0
|
$out->((1)) .= $out->((1))->clip(@{$o->{c}}) |
|
1200
|
0
|
0
|
|
|
|
0
|
unless($o->{c}->[0] == $o->{c}->[1]); |
|
1201
|
|
|
|
|
|
|
|
|
1202
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $o->{stretch}; |
|
1203
|
0
|
0
|
|
|
|
0
|
$out->(0:1) /= $o->{oconv} if(defined $o->{oconv}); |
|
1204
|
|
|
|
|
|
|
|
|
1205
|
0
|
|
|
|
|
0
|
$out; |
|
1206
|
0
|
|
|
|
|
0
|
}; |
|
1207
|
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1209
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1210
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace? $d : $d->copy; |
|
1211
|
|
|
|
|
|
|
|
|
1212
|
0
|
0
|
|
|
|
0
|
$out->(0:1) *= $o->{oconv} if defined($o->{oconv}); |
|
1213
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{stretch}; |
|
1214
|
0
|
|
|
|
|
0
|
$out->((1)) .= (atan(exp($out->((1)))) - $PI/4)*2; |
|
1215
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
1216
|
|
|
|
|
|
|
|
|
1217
|
0
|
|
|
|
|
0
|
$out; |
|
1218
|
0
|
|
|
|
|
0
|
}; |
|
1219
|
|
|
|
|
|
|
|
|
1220
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
1221
|
|
|
|
|
|
|
} |
|
1222
|
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
###################################################################### |
|
1224
|
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
=head2 t_utm |
|
1226
|
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
=for usage |
|
1228
|
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
$t = t_utm(,); |
|
1230
|
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
=for ref |
|
1232
|
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
(Cartography) Universal Transverse Mercator projection (cylindrical) |
|
1234
|
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
This is the internationally used UTM projection, with 2 subzones |
|
1236
|
|
|
|
|
|
|
(North/South). The UTM zones are parametrized individually, so if you |
|
1237
|
|
|
|
|
|
|
want a Zone 30 map you should use C. By default you get |
|
1238
|
|
|
|
|
|
|
the northern subzone, so that locations in the southern hemisphere get |
|
1239
|
|
|
|
|
|
|
negative Y coordinates. If you select the southern subzone (with the |
|
1240
|
|
|
|
|
|
|
"subzone=>-1" option), you get offset southern UTM coordinates. |
|
1241
|
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
The 20-subzone military system is not yet supported. If/when it is |
|
1243
|
|
|
|
|
|
|
implemented, you will be able to enter "subzone=>[a-t]" to select a N/S |
|
1244
|
|
|
|
|
|
|
subzone. |
|
1245
|
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
Note that UTM is really a family of transverse Mercator projections |
|
1247
|
|
|
|
|
|
|
with different central meridia. Each zone properly extends for six |
|
1248
|
|
|
|
|
|
|
degrees of longitude on either side of its appropriate central meridian, |
|
1249
|
|
|
|
|
|
|
with Zone 1 being centered at -177 degrees longitude (177 west). |
|
1250
|
|
|
|
|
|
|
Properly speaking, the zones only extend from 80 degrees south to 84 degrees |
|
1251
|
|
|
|
|
|
|
north; but this implementation lets you go all the way to 90 degrees. |
|
1252
|
|
|
|
|
|
|
The default UTM coordinates are meters. The origin for each zone is |
|
1253
|
|
|
|
|
|
|
on the equator, at an easting of -500,000 meters. |
|
1254
|
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
The default output units are meters, assuming that you are wanting a |
|
1256
|
|
|
|
|
|
|
map of the Earth. This will break for bodies other than Earth (which |
|
1257
|
|
|
|
|
|
|
have different radii and hence different conversions between lat/lon |
|
1258
|
|
|
|
|
|
|
angle and meters). |
|
1259
|
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
The standard UTM projection has a slight reduction in scale at the |
|
1261
|
|
|
|
|
|
|
prime meridian of each zone: the transverse Mercator projection's |
|
1262
|
|
|
|
|
|
|
standard "parallels" are 180km e/w of the central meridian. However, |
|
1263
|
|
|
|
|
|
|
many Europeans prefer the "Gauss-Kruger" system, which is virtually |
|
1264
|
|
|
|
|
|
|
identical to UTM but with a normal tangent Mercator (standard parallel |
|
1265
|
|
|
|
|
|
|
on the prime meridian). To get this behavior, set "gk=>1". |
|
1266
|
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
Like the rest of the PDLA::Transform::Cartography package, t_utm uses a |
|
1268
|
|
|
|
|
|
|
spherical datum rather than the "official" ellipsoidal datums for the |
|
1269
|
|
|
|
|
|
|
UTM system. |
|
1270
|
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
This implementation was derived from the rather nice description by |
|
1272
|
|
|
|
|
|
|
Denis J. Dean, located on the web at: |
|
1273
|
|
|
|
|
|
|
http://www.cnr.colostate.edu/class_info/nr502/lg3/datums_coordinates/utm.html |
|
1274
|
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
OPTIONS |
|
1276
|
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
=over 3 |
|
1278
|
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=item STANDARD OPTIONS |
|
1280
|
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
(No positional options -- Origin and Roll are ignored) |
|
1282
|
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
=item ou, ounit, OutputUnit (default 'meters') |
|
1284
|
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
(This is likely to become a standard option in a future release) The |
|
1286
|
|
|
|
|
|
|
unit of the output map. By default, this is 'meters' for UTM, but you |
|
1287
|
|
|
|
|
|
|
may specify 'deg' or 'km' or even (heaven help us) 'miles' if you |
|
1288
|
|
|
|
|
|
|
prefer. |
|
1289
|
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
=item sz, subzone, SubZone (default 1) |
|
1291
|
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
Set this to -1 for the southern hemisphere subzone. Ultimately you |
|
1293
|
|
|
|
|
|
|
should be able to set it to a letter to get the corresponding military |
|
1294
|
|
|
|
|
|
|
subzone, but that's too much effort for now. |
|
1295
|
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
=item gk, gausskruger (default 0) |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
Set this to 1 to get the (European-style) tangent-plane Mercator with |
|
1299
|
|
|
|
|
|
|
standard parallel on the prime meridian. The default of 0 places the |
|
1300
|
|
|
|
|
|
|
standard parallels 180km east/west of the prime meridian, yielding better |
|
1301
|
|
|
|
|
|
|
average scale across the zone. Setting gk=>1 makes the scale exactly 1.0 |
|
1302
|
|
|
|
|
|
|
at the central meridian, and >1.0 everywhere else on the projection. |
|
1303
|
|
|
|
|
|
|
The difference in scale is about 0.3%. |
|
1304
|
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
=back |
|
1306
|
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
=cut |
|
1308
|
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
sub t_utm { |
|
1310
|
0
|
|
|
0
|
1
|
0
|
my $zone = (int(shift)-1) % 60 + 1; |
|
1311
|
0
|
|
|
|
|
0
|
my($x) = _new(@_,"UTM-$zone"); |
|
1312
|
0
|
|
|
|
|
0
|
my $opt = $x->{options}; |
|
1313
|
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
## Make sure that there is a conversion (default is 'meters') |
|
1315
|
0
|
0
|
|
|
|
0
|
$x->{ounit} = ['meter','meter'] unless defined($x->{ounit}); |
|
1316
|
0
|
0
|
|
|
|
0
|
$x->{ounit} = [$x->{ounit},$x->{ounit}] unless ref($x->{ounit}); |
|
1317
|
0
|
|
|
|
|
0
|
$x->{params}->{oconv} = _uconv($x->{ounit}->[0]); |
|
1318
|
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
## Define our zone and NS offset |
|
1320
|
0
|
|
|
|
|
0
|
my $subzone = _opt($opt,['sz', 'subzone', 'SubZone'],1); |
|
1321
|
0
|
|
|
|
|
0
|
my $offset = zeroes(2); |
|
1322
|
0
|
|
|
|
|
0
|
$offset->(0) .= 5e5*(2*$PI/40e6)/$x->{params}->{oconv}; |
|
1323
|
0
|
0
|
|
|
|
0
|
$offset->(1) .= ($subzone < 0) ? $PI/2/$x->{params}->{oconv} : 0; |
|
1324
|
|
|
|
|
|
|
|
|
1325
|
0
|
|
|
|
|
0
|
my $merid = ($zone * 6) - 183; |
|
1326
|
|
|
|
|
|
|
|
|
1327
|
0
|
|
|
|
|
0
|
my $gk = _opt($opt,['gk','gausskruger'],0); |
|
1328
|
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
my($me) = t_compose(t_linear(post=>$offset, |
|
1330
|
|
|
|
|
|
|
rot=>-90 |
|
1331
|
|
|
|
|
|
|
), |
|
1332
|
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
t_mercator(o=>[$merid,0], |
|
1334
|
|
|
|
|
|
|
r=>90, |
|
1335
|
|
|
|
|
|
|
ou=>$x->{ounit}, |
|
1336
|
0
|
0
|
|
|
|
0
|
s=>$gk ? 0 : ($RAD2DEG * (180/6371)) |
|
1337
|
|
|
|
|
|
|
) |
|
1338
|
|
|
|
|
|
|
); |
|
1339
|
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
|
|
1341
|
0
|
0
|
|
|
|
0
|
my $s = ($zone < 0) ? "S Hemisphere " : ""; |
|
1342
|
0
|
|
|
|
|
0
|
$me->{otype} = ["UTM-$zone Easting","${s}Northing"]; |
|
1343
|
0
|
|
|
|
|
0
|
$me->{ounit} = $x->{ounit}; |
|
1344
|
|
|
|
|
|
|
|
|
1345
|
0
|
|
|
|
|
0
|
return $me; |
|
1346
|
|
|
|
|
|
|
} |
|
1347
|
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
###################################################################### |
|
1349
|
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
=head2 t_sin_lat |
|
1351
|
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
=for usage |
|
1353
|
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
$t = t_sin_lat(); |
|
1355
|
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
=for ref |
|
1357
|
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
(Cartography) Cyl. equal-area projection (cyl.; authalic) |
|
1359
|
|
|
|
|
|
|
|
|
1360
|
|
|
|
|
|
|
This projection is commonly used in solar Carrington plots; but not much |
|
1361
|
|
|
|
|
|
|
for terrestrial mapping. |
|
1362
|
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
OPTIONS |
|
1364
|
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
=over 3 |
|
1366
|
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1368
|
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
=item s,std, Standard (default 0) |
|
1370
|
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
This is the parallel at which the map is conformal. It is also conformal |
|
1372
|
|
|
|
|
|
|
at the parallel of opposite sign. The conformality is achieved by matched |
|
1373
|
|
|
|
|
|
|
vertical stretching and horizontal squishing (to achieve constant area). |
|
1374
|
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
=back |
|
1376
|
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
=cut |
|
1378
|
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
@PDLA::Transform::Cartography::SinLat::ISA = ('PDLA::Transform::Cartography'); |
|
1380
|
|
|
|
|
|
|
sub t_sin_lat { |
|
1381
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,"Sine-Latitude Projection"); |
|
1382
|
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
$me->{params}->{std} = topdl(_opt($me->{options}, |
|
1384
|
|
|
|
|
|
|
['s','std','standard','Standard'], |
|
1385
|
0
|
|
|
|
|
0
|
0))->at(0) * $me->{params}->{conv}; |
|
1386
|
|
|
|
|
|
|
|
|
1387
|
0
|
0
|
|
|
|
0
|
if($me->{params}->{std} == 0) { |
|
1388
|
0
|
|
|
|
|
0
|
$me->{otype} = ['longitude','sin latitude']; |
|
1389
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['radians',' ']; # nonzero but blank! |
|
1390
|
|
|
|
|
|
|
} else { |
|
1391
|
0
|
|
|
|
|
0
|
$me->{otype} = ['proj. longitude','proj. sin latitude']; |
|
1392
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['radians',' ']; |
|
1393
|
|
|
|
|
|
|
} |
|
1394
|
|
|
|
|
|
|
|
|
1395
|
0
|
|
|
|
|
0
|
$me->{params}->{stretch} = sqrt(cos($me->{params}->{std})); |
|
1396
|
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1398
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1399
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1400
|
|
|
|
|
|
|
|
|
1401
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $me->{params}->{conv}; |
|
1402
|
0
|
|
|
|
|
0
|
$out->((1)) .= sin($out->((1))) / $o->{stretch}; |
|
1403
|
0
|
|
|
|
|
0
|
$out->((0)) *= $o->{stretch}; |
|
1404
|
0
|
|
|
|
|
0
|
$out; |
|
1405
|
0
|
|
|
|
|
0
|
}; |
|
1406
|
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1408
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1409
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1410
|
0
|
|
|
|
|
0
|
$out->((1)) .= asin($out->((1)) * $o->{stretch}); |
|
1411
|
0
|
|
|
|
|
0
|
$out->((0)) /= $o->{stretch}; |
|
1412
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $me->{params}->{conv}; |
|
1413
|
0
|
|
|
|
|
0
|
$out; |
|
1414
|
0
|
|
|
|
|
0
|
}; |
|
1415
|
|
|
|
|
|
|
|
|
1416
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
1417
|
|
|
|
|
|
|
} |
|
1418
|
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
###################################################################### |
|
1420
|
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
=head2 t_sinusoidal |
|
1422
|
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
=for usage |
|
1424
|
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
$t = t_sinusoidal(); |
|
1426
|
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
=for ref |
|
1428
|
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
(Cartography) Sinusoidal projection (authalic) |
|
1430
|
|
|
|
|
|
|
|
|
1431
|
|
|
|
|
|
|
Sinusoidal projection preserves the latitude scale but scales |
|
1432
|
|
|
|
|
|
|
longitude according to sin(lat); in this respect it is the companion to |
|
1433
|
|
|
|
|
|
|
L, which is also authalic but preserves the |
|
1434
|
|
|
|
|
|
|
longitude scale instead. |
|
1435
|
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
OPTIONS |
|
1437
|
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
=over 3 |
|
1439
|
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1441
|
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
=back |
|
1443
|
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
=cut |
|
1445
|
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
sub t_sinusoidal { |
|
1447
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,"Sinusoidal Projection"); |
|
1448
|
0
|
|
|
|
|
0
|
$me->{otype} = ['longitude','latitude']; |
|
1449
|
0
|
|
|
|
|
0
|
$me->{ounit} = [' ','radians']; |
|
1450
|
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1452
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1453
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1454
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $o->{conv}; |
|
1455
|
|
|
|
|
|
|
|
|
1456
|
0
|
|
|
|
|
0
|
$out->((0)) *= cos($out->((1))); |
|
1457
|
0
|
|
|
|
|
0
|
$out; |
|
1458
|
0
|
|
|
|
|
0
|
}; |
|
1459
|
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1461
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1462
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1463
|
0
|
|
|
|
|
0
|
my($x) = $out->((0)); |
|
1464
|
0
|
|
|
|
|
0
|
my($y) = $out->((1)); |
|
1465
|
|
|
|
|
|
|
|
|
1466
|
0
|
|
|
|
|
0
|
$x /= cos($out->((1))); |
|
1467
|
|
|
|
|
|
|
|
|
1468
|
0
|
|
|
|
|
0
|
my($rej) = ( (abs($x)>$PI) | (abs($y)>($PI/2)) )->flat; |
|
1469
|
0
|
|
|
|
|
0
|
$x->flat->($rej) .= $o->{bad}; |
|
1470
|
0
|
|
|
|
|
0
|
$y->flat->($rej) .= $o->{bad}; |
|
1471
|
|
|
|
|
|
|
|
|
1472
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
1473
|
0
|
|
|
|
|
0
|
$out; |
|
1474
|
0
|
|
|
|
|
0
|
}; |
|
1475
|
|
|
|
|
|
|
|
|
1476
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
1477
|
|
|
|
|
|
|
} |
|
1478
|
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
###################################################################### |
|
1484
|
|
|
|
|
|
|
# |
|
1485
|
|
|
|
|
|
|
# Conic projections are subclassed for easier stringification and |
|
1486
|
|
|
|
|
|
|
# parsing of the standard parallels. The constructor gets copied |
|
1487
|
|
|
|
|
|
|
# into the current package for ease of hackage. |
|
1488
|
|
|
|
|
|
|
# |
|
1489
|
|
|
|
|
|
|
# This is a little kludgy -- it's intended for direct calling |
|
1490
|
|
|
|
|
|
|
# rather than method calling, and it puts its own class name on the |
|
1491
|
|
|
|
|
|
|
# front of the argument list. But, hey, it works... |
|
1492
|
|
|
|
|
|
|
# |
|
1493
|
|
|
|
|
|
|
@PDLA::Transform::Cartography::Conic::ISA = ('PDLA::Transform::Cartography'); |
|
1494
|
|
|
|
|
|
|
sub _c_new { |
|
1495
|
0
|
|
|
0
|
|
0
|
my($def_std) = pop; |
|
1496
|
0
|
|
|
|
|
0
|
my($me) = new('PDLA::Transform::Cartography::Conic',@_); |
|
1497
|
|
|
|
|
|
|
|
|
1498
|
0
|
|
|
|
|
0
|
my($p) = $me->{params}; |
|
1499
|
0
|
|
|
|
|
0
|
$p->{std} = _opt($me->{options},['s','std','standard','Standard'], |
|
1500
|
|
|
|
|
|
|
$def_std); |
|
1501
|
0
|
|
|
|
|
0
|
$p->{std} = topdl($p->{std}) * $me->{params}->{conv}; |
|
1502
|
|
|
|
|
|
|
$p->{std} = topdl([$PI/2 * ($p->{std}<0 ? -1 : 1), $p->{std}->at(0)]) |
|
1503
|
0
|
0
|
|
|
|
0
|
if($p->{std}->nelem == 1); |
|
|
|
0
|
|
|
|
|
|
|
1504
|
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
$me->{params}->{cylindrical} = 1 |
|
1506
|
0
|
0
|
|
|
|
0
|
if(approx($p->{std}->(0),-$p->{std}->(1))); |
|
1507
|
|
|
|
|
|
|
|
|
1508
|
0
|
|
|
|
|
0
|
$me; |
|
1509
|
|
|
|
|
|
|
} |
|
1510
|
|
|
|
|
|
|
|
|
1511
|
|
|
|
|
|
|
sub PDLA::Transform::Cartography::Conic::stringify { |
|
1512
|
0
|
|
|
0
|
|
0
|
my($me) = shift; |
|
1513
|
0
|
|
|
|
|
0
|
my($out) = $me->SUPER::stringify; |
|
1514
|
|
|
|
|
|
|
|
|
1515
|
|
|
|
|
|
|
$out .= sprintf("\tStd parallels: %6.2f,%6.2f %s\n", |
|
1516
|
|
|
|
|
|
|
$me->{params}->{std}->at(0) / $me->{params}->{conv}, |
|
1517
|
|
|
|
|
|
|
$me->{params}->{std}->at(1) / $me->{params}->{conv}, |
|
1518
|
0
|
|
|
|
|
0
|
$me->{params}->{u}); |
|
1519
|
0
|
|
|
|
|
0
|
$out; |
|
1520
|
|
|
|
|
|
|
} |
|
1521
|
|
|
|
|
|
|
|
|
1522
|
|
|
|
|
|
|
###################################################################### |
|
1523
|
|
|
|
|
|
|
|
|
1524
|
|
|
|
|
|
|
=head2 t_conic |
|
1525
|
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
=for usage |
|
1527
|
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
$t = t_conic() |
|
1529
|
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
=for ref |
|
1531
|
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
(Cartography) Simple conic projection (conic; equidistant) |
|
1533
|
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
This is the simplest conic projection, with parallels mapped to |
|
1535
|
|
|
|
|
|
|
equidistant concentric circles. It is neither authalic nor conformal. |
|
1536
|
|
|
|
|
|
|
This transformation is also referred to as the "Modified Transverse |
|
1537
|
|
|
|
|
|
|
Mercator" projection in several maps of Alaska published by the USGS; |
|
1538
|
|
|
|
|
|
|
and the American State of New Mexico re-invented the projection in |
|
1539
|
|
|
|
|
|
|
1936 for an official map of that State. |
|
1540
|
|
|
|
|
|
|
|
|
1541
|
|
|
|
|
|
|
OPTIONS |
|
1542
|
|
|
|
|
|
|
|
|
1543
|
|
|
|
|
|
|
=over 3 |
|
1544
|
|
|
|
|
|
|
|
|
1545
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1546
|
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
=item s, std, Standard (default 29.5, 45.5) |
|
1548
|
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
The locations of the standard parallel(s) (where the cone intersects |
|
1550
|
|
|
|
|
|
|
the surface of the sphere). If you specify only one then the other is |
|
1551
|
|
|
|
|
|
|
taken to be the nearest pole. If you specify both of them to be one |
|
1552
|
|
|
|
|
|
|
pole then you get an equidistant azimuthal map. If you specify both |
|
1553
|
|
|
|
|
|
|
of them to be opposite and equidistant from the equator you get a |
|
1554
|
|
|
|
|
|
|
Plate Caree projection. |
|
1555
|
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
=back |
|
1557
|
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
=cut |
|
1559
|
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
sub t_conic { |
|
1561
|
0
|
|
|
0
|
1
|
0
|
my($me) = _c_new(@_,"Simple Conic Projection",[29.5,45.5]); |
|
1562
|
|
|
|
|
|
|
|
|
1563
|
0
|
|
|
|
|
0
|
my($p) = $me->{params}; |
|
1564
|
|
|
|
|
|
|
|
|
1565
|
0
|
0
|
|
|
|
0
|
if($p->{cylindrical}) { |
|
1566
|
0
|
0
|
|
|
|
0
|
print STDERR "Simple conic: degenerate case; using Plate Caree\n" |
|
1567
|
|
|
|
|
|
|
if($PDLA::verbose); |
|
1568
|
0
|
|
|
|
|
0
|
return t_caree($me->{options}); |
|
1569
|
|
|
|
|
|
|
} |
|
1570
|
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
$p->{n} = ((cos($p->{std}->((0))) - cos($p->{std}->((1)))) |
|
1572
|
|
|
|
|
|
|
/ |
|
1573
|
0
|
|
|
|
|
0
|
($p->{std}->((1)) - $p->{std}->((0)))); |
|
1574
|
|
|
|
|
|
|
|
|
1575
|
0
|
|
|
|
|
0
|
$p->{G} = cos($p->{std}->((0)))/$p->{n} + $p->{std}->((0)); |
|
1576
|
|
|
|
|
|
|
|
|
1577
|
0
|
|
|
|
|
0
|
$me->{otype} = ['Conic X','Conic Y']; |
|
1578
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['Proj. radians','Proj. radians']; |
|
1579
|
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1581
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1582
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1583
|
|
|
|
|
|
|
|
|
1584
|
0
|
|
|
|
|
0
|
my($rho) = $o->{G} - $d->((1)) * $o->{conv}; |
|
1585
|
0
|
|
|
|
|
0
|
my($theta) = $o->{n} * $d->((0)) * $o->{conv}; |
|
1586
|
|
|
|
|
|
|
|
|
1587
|
0
|
|
|
|
|
0
|
$out->((0)) .= $rho * sin($theta); |
|
1588
|
0
|
|
|
|
|
0
|
$out->((1)) .= $o->{G} - $rho * cos($theta); |
|
1589
|
|
|
|
|
|
|
|
|
1590
|
0
|
|
|
|
|
0
|
$out; |
|
1591
|
0
|
|
|
|
|
0
|
}; |
|
1592
|
|
|
|
|
|
|
|
|
1593
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1594
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1595
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1596
|
|
|
|
|
|
|
|
|
1597
|
0
|
|
|
|
|
0
|
my($x) = $d->((0)); |
|
1598
|
0
|
|
|
|
|
0
|
my($y) = $o->{G} - $d->((1)); |
|
1599
|
0
|
|
|
|
|
0
|
my($rho) = sqrt($x*$x + $y*$y); |
|
1600
|
0
|
0
|
|
|
|
0
|
$rho *= -1 if($o->{n}<0); |
|
1601
|
|
|
|
|
|
|
|
|
1602
|
0
|
0
|
|
|
|
0
|
my($theta) = ($o->{n} < 0) ? atan2(-$x,-$y) : atan2($x,$y); |
|
1603
|
|
|
|
|
|
|
|
|
1604
|
0
|
|
|
|
|
0
|
$out->((1)) .= $o->{G} - $rho; |
|
1605
|
|
|
|
|
|
|
$out->((1))->where(($out->((1)) < -$PI/2) | ($out->((1)) > $PI/2)) |
|
1606
|
0
|
|
|
|
|
0
|
.= $o->{bad}; |
|
1607
|
|
|
|
|
|
|
|
|
1608
|
0
|
|
|
|
|
0
|
$out->((0)) .= $theta / $o->{n}; |
|
1609
|
|
|
|
|
|
|
$out->((0))->where(($out->((0)) < -$PI) | ($out->((0)) > $PI/2)) |
|
1610
|
0
|
|
|
|
|
0
|
.= $o->{bad}; |
|
1611
|
|
|
|
|
|
|
|
|
1612
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
1613
|
|
|
|
|
|
|
|
|
1614
|
0
|
|
|
|
|
0
|
$out; |
|
1615
|
0
|
|
|
|
|
0
|
}; |
|
1616
|
|
|
|
|
|
|
|
|
1617
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
1618
|
|
|
|
|
|
|
} |
|
1619
|
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
|
|
1621
|
|
|
|
|
|
|
|
|
1622
|
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
###################################################################### |
|
1624
|
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
=head2 t_albers |
|
1626
|
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
=for usage |
|
1628
|
|
|
|
|
|
|
|
|
1629
|
|
|
|
|
|
|
$t = t_albers() |
|
1630
|
|
|
|
|
|
|
|
|
1631
|
|
|
|
|
|
|
=for ref |
|
1632
|
|
|
|
|
|
|
|
|
1633
|
|
|
|
|
|
|
(Cartography) Albers conic projection (conic; authalic) |
|
1634
|
|
|
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
This is the standard projection used by the US Geological Survey for |
|
1636
|
|
|
|
|
|
|
sectionals of the 50 contiguous United States of America. |
|
1637
|
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
The projection reduces to the Lambert equal-area conic (infrequently |
|
1639
|
|
|
|
|
|
|
used and not to be confused with the Lambert conformal conic, |
|
1640
|
|
|
|
|
|
|
L!) if the pole is used as one of the two |
|
1641
|
|
|
|
|
|
|
standard parallels. |
|
1642
|
|
|
|
|
|
|
|
|
1643
|
|
|
|
|
|
|
Notionally, this is a conic projection onto a cone that intersects the |
|
1644
|
|
|
|
|
|
|
sphere at the two standard parallels; it works best when the two parallels |
|
1645
|
|
|
|
|
|
|
straddle the region of interest. |
|
1646
|
|
|
|
|
|
|
|
|
1647
|
|
|
|
|
|
|
OPTIONS |
|
1648
|
|
|
|
|
|
|
|
|
1649
|
|
|
|
|
|
|
=over 3 |
|
1650
|
|
|
|
|
|
|
|
|
1651
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1652
|
|
|
|
|
|
|
|
|
1653
|
|
|
|
|
|
|
=item s, std, standard, Standard (default (29.5,45.5)) |
|
1654
|
|
|
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
The locations of the standard parallel(s). If you specify only one then |
|
1656
|
|
|
|
|
|
|
the other is taken to be the nearest pole and a Lambert Equal-Area Conic |
|
1657
|
|
|
|
|
|
|
map results. If you specify both standard parallels to be the same pole, |
|
1658
|
|
|
|
|
|
|
then the projection reduces to the Lambert Azimuthal Equal-Area map as |
|
1659
|
|
|
|
|
|
|
aq special case. (Note that L is Lambert's |
|
1660
|
|
|
|
|
|
|
Conformal Conic, the most commonly used of Lambert's projections.) |
|
1661
|
|
|
|
|
|
|
|
|
1662
|
|
|
|
|
|
|
The default values for the standard parallels are those chosen by Adams |
|
1663
|
|
|
|
|
|
|
for maps of the lower 48 US states: (29.5,45.5). The USGS recommends |
|
1664
|
|
|
|
|
|
|
(55,65) for maps of Alaska and (8,18) for maps of Hawaii -- these latter |
|
1665
|
|
|
|
|
|
|
are chosen to also include the Canal Zone and Philippine Islands farther |
|
1666
|
|
|
|
|
|
|
south, which is why both of those parallels are south of the Hawaiian islands. |
|
1667
|
|
|
|
|
|
|
|
|
1668
|
|
|
|
|
|
|
The transformation reduces to the cylindrical equal-area (sin-lat) |
|
1669
|
|
|
|
|
|
|
transformation in the case where the standard parallels are opposite and |
|
1670
|
|
|
|
|
|
|
equidistant from the equator, and in fact this is implemented by a call to |
|
1671
|
|
|
|
|
|
|
t_sin_lat. |
|
1672
|
|
|
|
|
|
|
|
|
1673
|
|
|
|
|
|
|
=back |
|
1674
|
|
|
|
|
|
|
|
|
1675
|
|
|
|
|
|
|
=cut |
|
1676
|
|
|
|
|
|
|
|
|
1677
|
|
|
|
|
|
|
sub t_albers { |
|
1678
|
0
|
|
|
0
|
1
|
0
|
my($me) = _c_new(@_,"Albers Equal-Area Conic Projection",[29.5,45.5]); |
|
1679
|
|
|
|
|
|
|
|
|
1680
|
0
|
|
|
|
|
0
|
my($p) = $me->{params}; |
|
1681
|
|
|
|
|
|
|
|
|
1682
|
0
|
0
|
|
|
|
0
|
if($p->{cylindrical}) { |
|
1683
|
0
|
0
|
|
|
|
0
|
print STDERR "Albers equal-area conic: degenerate case; using equal-area cylindrical\n" |
|
1684
|
|
|
|
|
|
|
if($PDLA::verbose); |
|
1685
|
0
|
|
|
|
|
0
|
return t_sin_lat($me->{options}); |
|
1686
|
|
|
|
|
|
|
} |
|
1687
|
|
|
|
|
|
|
|
|
1688
|
0
|
|
|
|
|
0
|
$p->{n} = sin($p->{std})->sumover / 2; |
|
1689
|
|
|
|
|
|
|
$p->{C} = (cos($p->{std}->((1)))*cos($p->{std}->((1))) + |
|
1690
|
0
|
|
|
|
|
0
|
2 * $p->{n} * sin($p->{std}->((1))) ); |
|
1691
|
0
|
|
|
|
|
0
|
$p->{rho0} = sqrt($p->{C}) / $p->{n}; |
|
1692
|
|
|
|
|
|
|
|
|
1693
|
0
|
|
|
|
|
0
|
$me->{otype} = ['Conic X','Conic Y']; |
|
1694
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['Proj. radians','Proj. radians']; |
|
1695
|
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1697
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1698
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1699
|
|
|
|
|
|
|
|
|
1700
|
0
|
|
|
|
|
0
|
my($rho) = sqrt( $o->{C} - 2 * $o->{n} * sin($d->((1)) * $o->{conv}) ) / $o->{n}; |
|
1701
|
0
|
|
|
|
|
0
|
my($theta) = $o->{n} * $d->((0)) * $o->{conv}; |
|
1702
|
|
|
|
|
|
|
|
|
1703
|
0
|
|
|
|
|
0
|
$out->((0)) .= $rho * sin($theta); |
|
1704
|
0
|
|
|
|
|
0
|
$out->((1)) .= $p->{rho0} - $rho * cos($theta); |
|
1705
|
0
|
|
|
|
|
0
|
$out; |
|
1706
|
0
|
|
|
|
|
0
|
}; |
|
1707
|
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1709
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1710
|
|
|
|
|
|
|
|
|
1711
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1712
|
|
|
|
|
|
|
|
|
1713
|
0
|
|
|
|
|
0
|
my($x) = $d->((0)); |
|
1714
|
0
|
|
|
|
|
0
|
my($y) = $o->{rho0} - $d->((1)); |
|
1715
|
|
|
|
|
|
|
|
|
1716
|
0
|
0
|
|
|
|
0
|
my($theta) = ($o->{n} < 0) ? atan2 -$x,-$y : atan2 $x, $y; |
|
1717
|
0
|
|
|
|
|
0
|
my($rho) = sqrt( $x*$x + $y*$y ) * $o->{n}; |
|
1718
|
|
|
|
|
|
|
|
|
1719
|
0
|
|
|
|
|
0
|
$out->((1)) .= asin( ( $o->{C} - ( $rho * $rho ) ) / (2 * $o->{n}) ); |
|
1720
|
|
|
|
|
|
|
|
|
1721
|
0
|
|
|
|
|
0
|
$out->((0)) .= $theta / $o->{n}; |
|
1722
|
0
|
|
|
|
|
0
|
$out->((0))->where(($out->((0))>$PI) | ($out->((0))<-$PI)) .= $o->{bad}; |
|
1723
|
|
|
|
|
|
|
|
|
1724
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
1725
|
|
|
|
|
|
|
|
|
1726
|
0
|
|
|
|
|
0
|
$out; |
|
1727
|
0
|
|
|
|
|
0
|
}; |
|
1728
|
|
|
|
|
|
|
|
|
1729
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
1730
|
|
|
|
|
|
|
} |
|
1731
|
|
|
|
|
|
|
|
|
1732
|
|
|
|
|
|
|
###################################################################### |
|
1733
|
|
|
|
|
|
|
|
|
1734
|
|
|
|
|
|
|
=head2 t_lambert |
|
1735
|
|
|
|
|
|
|
|
|
1736
|
|
|
|
|
|
|
=for usage |
|
1737
|
|
|
|
|
|
|
|
|
1738
|
|
|
|
|
|
|
$t = t_lambert(); |
|
1739
|
|
|
|
|
|
|
|
|
1740
|
|
|
|
|
|
|
=for ref |
|
1741
|
|
|
|
|
|
|
|
|
1742
|
|
|
|
|
|
|
(Cartography) Lambert conic projection (conic; conformal) |
|
1743
|
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
Lambert conformal conic projection is widely used in aeronautical |
|
1745
|
|
|
|
|
|
|
charts and state base maps published by the USA's FAA and USGS. It's |
|
1746
|
|
|
|
|
|
|
especially useful for mid-latitude charts. In particular, straight lines |
|
1747
|
|
|
|
|
|
|
approximate (but are not exactly) great circle routes of up to ~2 radians. |
|
1748
|
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
The default standard parallels are 33 and 45 to match the USGS state |
|
1750
|
|
|
|
|
|
|
1:500,000 base maps of the United States. At scales of 1:500,000 and |
|
1751
|
|
|
|
|
|
|
larger, discrepancies between the spherical and ellipsoidal projections |
|
1752
|
|
|
|
|
|
|
become important; use care with this projection on spheres. |
|
1753
|
|
|
|
|
|
|
|
|
1754
|
|
|
|
|
|
|
OPTIONS |
|
1755
|
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
=over 3 |
|
1757
|
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1759
|
|
|
|
|
|
|
|
|
1760
|
|
|
|
|
|
|
=item s, std, standard, Standard (default (33,45)) |
|
1761
|
|
|
|
|
|
|
|
|
1762
|
|
|
|
|
|
|
The locations of the standard parallel(s) for the conic projection. |
|
1763
|
|
|
|
|
|
|
The transform reduces to the Mercator projection in the case where the |
|
1764
|
|
|
|
|
|
|
standard parallels are opposite and equidistant from the equator, and |
|
1765
|
|
|
|
|
|
|
in fact this is implemented by a call to t_mercator. |
|
1766
|
|
|
|
|
|
|
|
|
1767
|
|
|
|
|
|
|
=item c, clip, Clip (default [-75,75]) |
|
1768
|
|
|
|
|
|
|
|
|
1769
|
|
|
|
|
|
|
Because the transform is conformal, the distant pole is displaced to |
|
1770
|
|
|
|
|
|
|
infinity. Many applications require a clipping boundary. The value |
|
1771
|
|
|
|
|
|
|
is in whatever angular unit you set with the standard 'unit' option. |
|
1772
|
|
|
|
|
|
|
For consistency with L, clipping works the same |
|
1773
|
|
|
|
|
|
|
way even though in most cases only one pole needs it. Set this to 0 |
|
1774
|
|
|
|
|
|
|
for no clipping at all. |
|
1775
|
|
|
|
|
|
|
|
|
1776
|
|
|
|
|
|
|
=back |
|
1777
|
|
|
|
|
|
|
|
|
1778
|
|
|
|
|
|
|
=cut |
|
1779
|
|
|
|
|
|
|
|
|
1780
|
|
|
|
|
|
|
sub t_lambert { |
|
1781
|
0
|
|
|
0
|
1
|
0
|
my($me)= _c_new(@_,"Lambert Conformal Conic Projection",[33,45]); |
|
1782
|
0
|
|
|
|
|
0
|
my($p) = $me->{params}; |
|
1783
|
|
|
|
|
|
|
|
|
1784
|
0
|
0
|
|
|
|
0
|
if($p->{cylindrical}){ |
|
1785
|
0
|
0
|
|
|
|
0
|
print STDERR "Lambert conformal conic: std parallels are opposite & equal; using Mercator\n" |
|
1786
|
|
|
|
|
|
|
if($PDLA::verbose); |
|
1787
|
0
|
|
|
|
|
0
|
return t_mercator($me->{options}); |
|
1788
|
|
|
|
|
|
|
} |
|
1789
|
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
# Find clipping parallels |
|
1791
|
0
|
|
|
|
|
0
|
$p->{c} = _opt($me->{options},['c','clip','Clip'],undef); |
|
1792
|
0
|
0
|
|
|
|
0
|
if(defined($p->{c})) { |
|
1793
|
0
|
|
|
|
|
0
|
$p->{c} = topdl($p->{c}); |
|
1794
|
|
|
|
|
|
|
} else { |
|
1795
|
0
|
|
|
|
|
0
|
$p->{c} = topdl([-75,75]); |
|
1796
|
|
|
|
|
|
|
} |
|
1797
|
0
|
0
|
|
|
|
0
|
$p->{c} = abs($p->{c}) * topdl([-1,1]) if($p->{c}->nelem == 1); |
|
1798
|
0
|
|
|
|
|
0
|
$p->{c} = [$p->{c}->list]; |
|
1799
|
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
# Prefrobnicate |
|
1801
|
0
|
0
|
|
|
|
0
|
if(approx($p->{std}->((0)),$p->{std}->((1)))) { |
|
1802
|
0
|
|
|
|
|
0
|
$p->{n} = sin($p->{std}->((0))); |
|
1803
|
|
|
|
|
|
|
} else { |
|
1804
|
|
|
|
|
|
|
$p->{n} = (log(cos($p->{std}->((0)))/cos($p->{std}->((1)))) |
|
1805
|
|
|
|
|
|
|
/ |
|
1806
|
|
|
|
|
|
|
log( tan( $PI/4 + $p->{std}->((1))/2 ) |
|
1807
|
|
|
|
|
|
|
/ |
|
1808
|
0
|
|
|
|
|
0
|
tan( $PI/4 + $p->{std}->((0))/2 ) |
|
1809
|
|
|
|
|
|
|
) |
|
1810
|
|
|
|
|
|
|
); |
|
1811
|
|
|
|
|
|
|
} |
|
1812
|
|
|
|
|
|
|
|
|
1813
|
|
|
|
|
|
|
$p->{F} = ( cos($p->{std}->((0))) |
|
1814
|
|
|
|
|
|
|
* |
|
1815
|
|
|
|
|
|
|
( tan( $PI/4 + $p->{std}->((0))/2 ) ** $p->{n} ) / $p->{n} |
|
1816
|
0
|
|
|
|
|
0
|
); |
|
1817
|
|
|
|
|
|
|
|
|
1818
|
0
|
|
|
|
|
0
|
$p->{rho0} = $p->{F}; |
|
1819
|
|
|
|
|
|
|
|
|
1820
|
0
|
|
|
|
|
0
|
$me->{otype} = ['Conic X','Conic Y']; |
|
1821
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['Proj. radians','Proj. radians']; |
|
1822
|
|
|
|
|
|
|
|
|
1823
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1824
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1825
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1826
|
|
|
|
|
|
|
|
|
1827
|
|
|
|
|
|
|
my($cl) = ( ($o->{c}->[0] == $o->{c}->[1]) ? |
|
1828
|
|
|
|
|
|
|
$d->((1))*$o->{conv} : |
|
1829
|
0
|
|
|
|
|
0
|
($d->((1))->clip(@{$o->{c}}) * $o->{conv}) |
|
1830
|
0
|
0
|
|
|
|
0
|
); |
|
1831
|
|
|
|
|
|
|
|
|
1832
|
0
|
|
|
|
|
0
|
my($rho) = $o->{F} / ( tan($PI/4 + ($cl)/2 ) ** $o->{n} ); |
|
1833
|
0
|
|
|
|
|
0
|
my($theta) = $o->{n} * $d->((0)) * $o->{conv}; |
|
1834
|
|
|
|
|
|
|
|
|
1835
|
0
|
|
|
|
|
0
|
$out->((0)) .= $rho * sin($theta); |
|
1836
|
0
|
|
|
|
|
0
|
$out->((1)) .= $o->{rho0} - $rho * cos($theta); |
|
1837
|
0
|
|
|
|
|
0
|
$out; |
|
1838
|
0
|
|
|
|
|
0
|
}; |
|
1839
|
|
|
|
|
|
|
|
|
1840
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1841
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1842
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1843
|
|
|
|
|
|
|
|
|
1844
|
0
|
|
|
|
|
0
|
my($x) = $d->((0)); |
|
1845
|
0
|
|
|
|
|
0
|
my($y) = $o->{rho0} - $d->((1)); |
|
1846
|
|
|
|
|
|
|
|
|
1847
|
0
|
|
|
|
|
0
|
my($rho) = sqrt($x * $x + $y * $y); |
|
1848
|
0
|
0
|
|
|
|
0
|
$rho *= -1 if($o->{n} < 0); |
|
1849
|
0
|
0
|
|
|
|
0
|
my($theta) = ($o->{n} < 0) ? atan2(-$x,-$y):(atan2 $x,$y); |
|
1850
|
|
|
|
|
|
|
|
|
1851
|
|
|
|
|
|
|
|
|
1852
|
0
|
|
|
|
|
0
|
$out->((0)) .= $theta / $o->{n}; |
|
1853
|
0
|
|
|
|
|
0
|
$out->((0))->where(($out->((0)) > $PI) | ($out->((0)) < -$PI)) .= $o->{bad}; |
|
1854
|
|
|
|
|
|
|
|
|
1855
|
|
|
|
|
|
|
|
|
1856
|
0
|
|
|
|
|
0
|
$out->((1)) .= 2 * atan(($o->{F}/$rho)**(1.0/$o->{n})) - $PI/2; |
|
1857
|
0
|
|
|
|
|
0
|
$out->((1))->where(($out->((1)) > $PI/2) | ($out->((1)) < -$PI/2)) .= $o->{bad}; |
|
1858
|
|
|
|
|
|
|
|
|
1859
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
1860
|
|
|
|
|
|
|
|
|
1861
|
0
|
|
|
|
|
0
|
$out; |
|
1862
|
0
|
|
|
|
|
0
|
}; |
|
1863
|
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
|
|
1865
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
1866
|
|
|
|
|
|
|
} |
|
1867
|
|
|
|
|
|
|
|
|
1868
|
|
|
|
|
|
|
###################################################################### |
|
1869
|
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
=head2 t_stereographic |
|
1871
|
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
=for usage |
|
1873
|
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
$t = t_stereographic(); |
|
1875
|
|
|
|
|
|
|
|
|
1876
|
|
|
|
|
|
|
=for ref |
|
1877
|
|
|
|
|
|
|
|
|
1878
|
|
|
|
|
|
|
(Cartography) Stereographic projection (az.; conf.; persp.) |
|
1879
|
|
|
|
|
|
|
|
|
1880
|
|
|
|
|
|
|
The stereographic projection is a true perspective (planar) projection |
|
1881
|
|
|
|
|
|
|
from a point on the spherical surface opposite the origin of the map. |
|
1882
|
|
|
|
|
|
|
|
|
1883
|
|
|
|
|
|
|
OPTIONS |
|
1884
|
|
|
|
|
|
|
|
|
1885
|
|
|
|
|
|
|
=over 3 |
|
1886
|
|
|
|
|
|
|
|
|
1887
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1888
|
|
|
|
|
|
|
|
|
1889
|
|
|
|
|
|
|
=item c, clip, Clip (default 120) |
|
1890
|
|
|
|
|
|
|
|
|
1891
|
|
|
|
|
|
|
This is the angular distance from the center to the edge of the |
|
1892
|
|
|
|
|
|
|
projected map. The default 120 degrees gives you most of the opposite |
|
1893
|
|
|
|
|
|
|
hemisphere but avoids the hugely distorted part near the antipodes. |
|
1894
|
|
|
|
|
|
|
|
|
1895
|
|
|
|
|
|
|
=back |
|
1896
|
|
|
|
|
|
|
|
|
1897
|
|
|
|
|
|
|
=cut |
|
1898
|
|
|
|
|
|
|
|
|
1899
|
|
|
|
|
|
|
sub t_stereographic { |
|
1900
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,"Stereographic Projection"); |
|
1901
|
|
|
|
|
|
|
|
|
1902
|
0
|
|
|
|
|
0
|
$me->{params}->{k0} = 1.0; |
|
1903
|
|
|
|
|
|
|
$me->{params}->{c} = _opt($me->{options}, |
|
1904
|
|
|
|
|
|
|
['c','clip','Clip'], |
|
1905
|
0
|
|
|
|
|
0
|
120) * $me->{params}->{conv}; |
|
1906
|
|
|
|
|
|
|
|
|
1907
|
0
|
|
|
|
|
0
|
$me->{otype} = ['Stereo X','Stereo Y']; |
|
1908
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['Proj. body radii','Proj. radians']; |
|
1909
|
|
|
|
|
|
|
|
|
1910
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1911
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1912
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1913
|
|
|
|
|
|
|
|
|
1914
|
|
|
|
|
|
|
my($th,$ph) = ($out->((0)) * $o->{conv}, |
|
1915
|
0
|
|
|
|
|
0
|
$out->((1)) * $o->{conv}); |
|
1916
|
|
|
|
|
|
|
|
|
1917
|
0
|
|
|
|
|
0
|
my($cph) = cos($ph); # gets re-used |
|
1918
|
0
|
|
|
|
|
0
|
my($k) = 2 * $o->{k0} / (1 + cos($th) * $cph); |
|
1919
|
0
|
|
|
|
|
0
|
$out->((0)) .= $k * $cph * sin($th); |
|
1920
|
0
|
|
|
|
|
0
|
$out->((1)) .= $k * sin($ph); |
|
1921
|
|
|
|
|
|
|
|
|
1922
|
0
|
|
|
|
|
0
|
my($cl0) = 2*$o->{k0} / (1 + cos($o->{c})); |
|
1923
|
0
|
|
|
|
|
0
|
$out->((0))->where($k>$cl0) .= $o->{bad}; |
|
1924
|
0
|
|
|
|
|
0
|
$out->((1))->where($k>$cl0) .= $o->{bad}; |
|
1925
|
0
|
|
|
|
|
0
|
$out; |
|
1926
|
0
|
|
|
|
|
0
|
}; |
|
1927
|
|
|
|
|
|
|
|
|
1928
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
1929
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1930
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1931
|
|
|
|
|
|
|
|
|
1932
|
0
|
|
|
|
|
0
|
my($x) = $d->((0)); |
|
1933
|
0
|
|
|
|
|
0
|
my($y) = $d->((1)); |
|
1934
|
0
|
|
|
|
|
0
|
my($rho) = sqrt($x*$x + $y*$y); |
|
1935
|
0
|
|
|
|
|
0
|
my($c) = 2 * atan2($rho,2*$o->{k0}); |
|
1936
|
|
|
|
|
|
|
|
|
1937
|
0
|
|
|
|
|
0
|
$out->((0)) .= atan2($x * sin($c), $rho * cos($c)); |
|
1938
|
0
|
|
|
|
|
0
|
$out->((1)) .= asin($y * sin($c) / $rho); |
|
1939
|
|
|
|
|
|
|
|
|
1940
|
0
|
|
|
|
|
0
|
$out ->(0:1) /= $o->{conv}; |
|
1941
|
0
|
|
|
|
|
0
|
$out; |
|
1942
|
0
|
|
|
|
|
0
|
}; |
|
1943
|
|
|
|
|
|
|
|
|
1944
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
1945
|
|
|
|
|
|
|
} |
|
1946
|
|
|
|
|
|
|
|
|
1947
|
|
|
|
|
|
|
###################################################################### |
|
1948
|
|
|
|
|
|
|
|
|
1949
|
|
|
|
|
|
|
=head2 t_gnomonic |
|
1950
|
|
|
|
|
|
|
|
|
1951
|
|
|
|
|
|
|
=for usage |
|
1952
|
|
|
|
|
|
|
|
|
1953
|
|
|
|
|
|
|
$t = t_gnomonic(); |
|
1954
|
|
|
|
|
|
|
|
|
1955
|
|
|
|
|
|
|
=for ref |
|
1956
|
|
|
|
|
|
|
|
|
1957
|
|
|
|
|
|
|
(Cartography) Gnomonic (focal-plane) projection (az.; persp.) |
|
1958
|
|
|
|
|
|
|
|
|
1959
|
|
|
|
|
|
|
The gnomonic projection projects a hemisphere onto a tangent plane. |
|
1960
|
|
|
|
|
|
|
It is useful in cartography for the property that straight lines are |
|
1961
|
|
|
|
|
|
|
great circles; and it is useful in scientific imaging because |
|
1962
|
|
|
|
|
|
|
it is the projection generated by a simple optical system with a flat |
|
1963
|
|
|
|
|
|
|
focal plane. |
|
1964
|
|
|
|
|
|
|
|
|
1965
|
|
|
|
|
|
|
OPTIONS |
|
1966
|
|
|
|
|
|
|
|
|
1967
|
|
|
|
|
|
|
=over 3 |
|
1968
|
|
|
|
|
|
|
|
|
1969
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
1970
|
|
|
|
|
|
|
|
|
1971
|
|
|
|
|
|
|
=item c, clip, Clip (default 75) |
|
1972
|
|
|
|
|
|
|
|
|
1973
|
|
|
|
|
|
|
This is the angular distance from the center to the edge of the |
|
1974
|
|
|
|
|
|
|
projected map. The default 75 degrees gives you most of the |
|
1975
|
|
|
|
|
|
|
hemisphere but avoids the hugely distorted part near the horizon. |
|
1976
|
|
|
|
|
|
|
|
|
1977
|
|
|
|
|
|
|
=back |
|
1978
|
|
|
|
|
|
|
|
|
1979
|
|
|
|
|
|
|
=cut |
|
1980
|
|
|
|
|
|
|
|
|
1981
|
|
|
|
|
|
|
sub t_gnomonic { |
|
1982
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,"Gnomonic Projection"); |
|
1983
|
|
|
|
|
|
|
|
|
1984
|
0
|
|
|
|
|
0
|
$me->{params}->{k0} = 1.0; # Useful for standard parallel (TBD: add one) |
|
1985
|
|
|
|
|
|
|
|
|
1986
|
|
|
|
|
|
|
$me->{params}->{c} = topdl(_opt($me->{options}, |
|
1987
|
|
|
|
|
|
|
['c','clip','Clip'], |
|
1988
|
0
|
|
|
|
|
0
|
75) * $me->{params}->{conv}); |
|
1989
|
|
|
|
|
|
|
|
|
1990
|
0
|
|
|
|
|
0
|
$me->{params}->{c} .= $me->{params}->{c}->clip(undef,(90-1e-6)*$me->{params}->{conv}); |
|
1991
|
|
|
|
|
|
|
|
|
1992
|
0
|
|
|
|
|
0
|
$me->{otype} = ['Tangent-plane X','Tangent-plane Y']; |
|
1993
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['Proj. radians','Proj. radians']; |
|
1994
|
|
|
|
|
|
|
|
|
1995
|
|
|
|
|
|
|
$me->{func} = sub { |
|
1996
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
1997
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
1998
|
|
|
|
|
|
|
|
|
1999
|
|
|
|
|
|
|
my($th,$ph) = ($out->((0)) * $o->{conv}, |
|
2000
|
0
|
|
|
|
|
0
|
$out->((1)) * $o->{conv}); |
|
2001
|
|
|
|
|
|
|
|
|
2002
|
0
|
|
|
|
|
0
|
my($cph) = cos($ph); # gets re-used |
|
2003
|
|
|
|
|
|
|
|
|
2004
|
0
|
|
|
|
|
0
|
my($k) = $o->{k0} / (cos($th) * $cph); |
|
2005
|
0
|
|
|
|
|
0
|
my($cl0) = $o->{k0} / (cos($o->{c})); |
|
2006
|
|
|
|
|
|
|
|
|
2007
|
0
|
|
|
|
|
0
|
$out->((0)) .= $k * $cph * sin($th); |
|
2008
|
0
|
|
|
|
|
0
|
$out->((1)) .= $k * sin($ph); |
|
2009
|
|
|
|
|
|
|
|
|
2010
|
0
|
|
|
|
|
0
|
my $idx = whichND(($k > $cl0) | ($k < 0) | (!isfinite($k))); |
|
2011
|
0
|
0
|
|
|
|
0
|
if($idx->nelem) { |
|
2012
|
0
|
|
|
|
|
0
|
$out->((0))->range($idx) .= $o->{bad}; |
|
2013
|
0
|
|
|
|
|
0
|
$out->((1))->range($idx) .= $o->{bad}; |
|
2014
|
|
|
|
|
|
|
} |
|
2015
|
|
|
|
|
|
|
|
|
2016
|
0
|
|
|
|
|
0
|
$out; |
|
2017
|
0
|
|
|
|
|
0
|
}; |
|
2018
|
|
|
|
|
|
|
|
|
2019
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
2020
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2021
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2022
|
|
|
|
|
|
|
|
|
2023
|
0
|
|
|
|
|
0
|
my($x) = $d->((0)); |
|
2024
|
0
|
|
|
|
|
0
|
my($y) = $d->((1)); |
|
2025
|
|
|
|
|
|
|
|
|
2026
|
0
|
|
|
|
|
0
|
my($rho) = sqrt($x*$x + $y*$y); |
|
2027
|
0
|
|
|
|
|
0
|
my($c) = atan($rho/$o->{k0}); |
|
2028
|
|
|
|
|
|
|
|
|
2029
|
0
|
|
|
|
|
0
|
$out->((0)) .= atan2($x * sin($c), $rho * cos($c)); |
|
2030
|
0
|
|
|
|
|
0
|
$out->((1)) .= asin($y * sin($c) / $rho); |
|
2031
|
|
|
|
|
|
|
|
|
2032
|
0
|
|
|
|
|
0
|
my $idx = whichND($rho==0); |
|
2033
|
|
|
|
|
|
|
|
|
2034
|
0
|
0
|
|
|
|
0
|
if($idx->nelem) { |
|
2035
|
0
|
|
|
|
|
0
|
$out->((0))->range($idx) .= 0; |
|
2036
|
0
|
|
|
|
|
0
|
$out->((1))->range($idx) .= 0; |
|
2037
|
|
|
|
|
|
|
} |
|
2038
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
2039
|
0
|
|
|
|
|
0
|
$out; |
|
2040
|
0
|
|
|
|
|
0
|
}; |
|
2041
|
|
|
|
|
|
|
|
|
2042
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
2043
|
|
|
|
|
|
|
} |
|
2044
|
|
|
|
|
|
|
|
|
2045
|
|
|
|
|
|
|
###################################################################### |
|
2046
|
|
|
|
|
|
|
|
|
2047
|
|
|
|
|
|
|
=head2 t_az_eqd |
|
2048
|
|
|
|
|
|
|
|
|
2049
|
|
|
|
|
|
|
=for usage |
|
2050
|
|
|
|
|
|
|
|
|
2051
|
|
|
|
|
|
|
$t = t_az_eqd(); |
|
2052
|
|
|
|
|
|
|
|
|
2053
|
|
|
|
|
|
|
=for ref |
|
2054
|
|
|
|
|
|
|
|
|
2055
|
|
|
|
|
|
|
(Cartography) Azimuthal equidistant projection (az.; equi.) |
|
2056
|
|
|
|
|
|
|
|
|
2057
|
|
|
|
|
|
|
Basic azimuthal projection preserving length along radial lines from |
|
2058
|
|
|
|
|
|
|
the origin (meridians, in the original polar aspect). Hence, both |
|
2059
|
|
|
|
|
|
|
azimuth and distance are correct for journeys beginning at the origin. |
|
2060
|
|
|
|
|
|
|
|
|
2061
|
|
|
|
|
|
|
Applied to the celestial sphere, this is the projection made by |
|
2062
|
|
|
|
|
|
|
fisheye lenses; it is also the projection into which C |
|
2063
|
|
|
|
|
|
|
puts perspective views. |
|
2064
|
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
The projected plane scale is normally taken to be planetary radii; |
|
2066
|
|
|
|
|
|
|
this is useful for cartographers but not so useful for scientific |
|
2067
|
|
|
|
|
|
|
observers. Setting the 't=>1' option causes the output scale to shift |
|
2068
|
|
|
|
|
|
|
to camera angular coordinates (the angular unit is determined by the |
|
2069
|
|
|
|
|
|
|
standard 'Units' option; default is degrees). |
|
2070
|
|
|
|
|
|
|
|
|
2071
|
|
|
|
|
|
|
OPTIONS |
|
2072
|
|
|
|
|
|
|
|
|
2073
|
|
|
|
|
|
|
=over 3 |
|
2074
|
|
|
|
|
|
|
|
|
2075
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
2076
|
|
|
|
|
|
|
|
|
2077
|
|
|
|
|
|
|
=item c, clip, Clip (default 180 degrees) |
|
2078
|
|
|
|
|
|
|
|
|
2079
|
|
|
|
|
|
|
The largest angle relative to the origin. Default is the whole sphere. |
|
2080
|
|
|
|
|
|
|
|
|
2081
|
|
|
|
|
|
|
=back |
|
2082
|
|
|
|
|
|
|
|
|
2083
|
|
|
|
|
|
|
=cut |
|
2084
|
|
|
|
|
|
|
|
|
2085
|
|
|
|
|
|
|
sub t_az_eqd { |
|
2086
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,"Equidistant Azimuthal Projection"); |
|
2087
|
|
|
|
|
|
|
|
|
2088
|
|
|
|
|
|
|
$me->{params}->{c} = topdl(_opt($me->{options}, |
|
2089
|
|
|
|
|
|
|
['c','clip','Clip'], |
|
2090
|
0
|
|
|
|
|
0
|
180) * $me->{params}->{conv}); |
|
2091
|
|
|
|
|
|
|
|
|
2092
|
0
|
|
|
|
|
0
|
$me->{otype} = ['X distance','Y distance']; |
|
2093
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['radians','radians']; |
|
2094
|
|
|
|
|
|
|
|
|
2095
|
|
|
|
|
|
|
$me->{func} = sub { |
|
2096
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2097
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2098
|
|
|
|
|
|
|
|
|
2099
|
0
|
|
|
|
|
0
|
my($ph) = $d->((1)) * $o->{conv}; |
|
2100
|
0
|
|
|
|
|
0
|
my($th) = $d->((0)) * $o->{conv}; |
|
2101
|
|
|
|
|
|
|
|
|
2102
|
0
|
|
|
|
|
0
|
my $cos_c = cos($ph) * cos($th); |
|
2103
|
0
|
|
|
|
|
0
|
my $c = acos($cos_c); |
|
2104
|
0
|
|
|
|
|
0
|
my $k = $c / sin($c); |
|
2105
|
0
|
|
|
|
|
0
|
$k->where($c==0) .= 1; |
|
2106
|
|
|
|
|
|
|
|
|
2107
|
0
|
|
|
|
|
0
|
my($x,$y) = ($out->((0)), $out->((1))); |
|
2108
|
|
|
|
|
|
|
|
|
2109
|
0
|
|
|
|
|
0
|
$x .= $k * cos($ph) * sin($th); |
|
2110
|
0
|
|
|
|
|
0
|
$y .= $k * sin($ph); |
|
2111
|
|
|
|
|
|
|
|
|
2112
|
0
|
|
|
|
|
0
|
my $idx = whichND($c > $o->{c}); |
|
2113
|
0
|
0
|
|
|
|
0
|
if($idx->nelem) { |
|
2114
|
0
|
|
|
|
|
0
|
$x->range($idx) .= $o->{bad}; |
|
2115
|
0
|
|
|
|
|
0
|
$y->range($idx) .= $o->{bad}; |
|
2116
|
|
|
|
|
|
|
} |
|
2117
|
|
|
|
|
|
|
|
|
2118
|
0
|
|
|
|
|
0
|
$out; |
|
2119
|
0
|
|
|
|
|
0
|
}; |
|
2120
|
|
|
|
|
|
|
|
|
2121
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
2122
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2123
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2124
|
0
|
|
|
|
|
0
|
my($x) = $d->((0)); |
|
2125
|
0
|
|
|
|
|
0
|
my($y) = $d->((1)); |
|
2126
|
|
|
|
|
|
|
|
|
2127
|
0
|
|
|
|
|
0
|
my $rho = sqrt(($d->(0:1)*$d->(0:1))->sumover); |
|
2128
|
|
|
|
|
|
|
# Order is important -- ((0)) overwrites $x if is_inplace! |
|
2129
|
0
|
|
|
|
|
0
|
$out->((0)) .= atan2( $x * sin($rho), $rho * cos $rho ); |
|
2130
|
0
|
|
|
|
|
0
|
$out->((1)) .= asin( $y * sin($rho) / $rho ); |
|
2131
|
|
|
|
|
|
|
|
|
2132
|
0
|
|
|
|
|
0
|
my $idx = whichND($rho == 0); |
|
2133
|
0
|
0
|
|
|
|
0
|
if($idx->nelem) { |
|
2134
|
0
|
|
|
|
|
0
|
$out->((0))->range($idx) .= 0; |
|
2135
|
0
|
|
|
|
|
0
|
$out->((1))->range($idx) .= 0; |
|
2136
|
|
|
|
|
|
|
} |
|
2137
|
|
|
|
|
|
|
|
|
2138
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
2139
|
|
|
|
|
|
|
|
|
2140
|
0
|
|
|
|
|
0
|
$out; |
|
2141
|
0
|
|
|
|
|
0
|
}; |
|
2142
|
|
|
|
|
|
|
|
|
2143
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
2144
|
|
|
|
|
|
|
} |
|
2145
|
|
|
|
|
|
|
|
|
2146
|
|
|
|
|
|
|
|
|
2147
|
|
|
|
|
|
|
###################################################################### |
|
2148
|
|
|
|
|
|
|
|
|
2149
|
|
|
|
|
|
|
=head2 t_az_eqa |
|
2150
|
|
|
|
|
|
|
|
|
2151
|
|
|
|
|
|
|
=for usage |
|
2152
|
|
|
|
|
|
|
|
|
2153
|
|
|
|
|
|
|
$t = t_az_eqa(); |
|
2154
|
|
|
|
|
|
|
|
|
2155
|
|
|
|
|
|
|
=for ref |
|
2156
|
|
|
|
|
|
|
|
|
2157
|
|
|
|
|
|
|
(Cartography) Azimuthal equal-area projection (az.; auth.) |
|
2158
|
|
|
|
|
|
|
|
|
2159
|
|
|
|
|
|
|
OPTIONS |
|
2160
|
|
|
|
|
|
|
|
|
2161
|
|
|
|
|
|
|
=over 3 |
|
2162
|
|
|
|
|
|
|
|
|
2163
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
2164
|
|
|
|
|
|
|
|
|
2165
|
|
|
|
|
|
|
=item c, clip, Clip (default 180 degrees) |
|
2166
|
|
|
|
|
|
|
|
|
2167
|
|
|
|
|
|
|
The largest angle relative to the origin. Default is the whole sphere. |
|
2168
|
|
|
|
|
|
|
|
|
2169
|
|
|
|
|
|
|
=back |
|
2170
|
|
|
|
|
|
|
|
|
2171
|
|
|
|
|
|
|
=cut |
|
2172
|
|
|
|
|
|
|
|
|
2173
|
|
|
|
|
|
|
sub t_az_eqa { |
|
2174
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,"Equal-Area Azimuthal Projection"); |
|
2175
|
|
|
|
|
|
|
|
|
2176
|
|
|
|
|
|
|
$me->{params}->{c} = topdl(_opt($me->{options}, |
|
2177
|
|
|
|
|
|
|
['c','clip','Clip'], |
|
2178
|
0
|
|
|
|
|
0
|
180) * $me->{params}->{conv}); |
|
2179
|
|
|
|
|
|
|
|
|
2180
|
0
|
|
|
|
|
0
|
$me->{otype} = ['Azimuthal X','Azimuthal Y']; |
|
2181
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['Proj. radians','Proj. radians']; |
|
2182
|
|
|
|
|
|
|
|
|
2183
|
|
|
|
|
|
|
$me->{func} = sub { |
|
2184
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2185
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2186
|
|
|
|
|
|
|
|
|
2187
|
0
|
|
|
|
|
0
|
my($ph) = $d->((1)) * $o->{conv}; |
|
2188
|
0
|
|
|
|
|
0
|
my($th) = $d->((0)) * $o->{conv}; |
|
2189
|
|
|
|
|
|
|
|
|
2190
|
0
|
|
|
|
|
0
|
my($c) = acos(cos($ph) * cos($th)); |
|
2191
|
0
|
|
|
|
|
0
|
my($rho) = 2 * sin($c/2); |
|
2192
|
0
|
|
|
|
|
0
|
my($k) = 1.0/cos($c/2); |
|
2193
|
|
|
|
|
|
|
|
|
2194
|
0
|
|
|
|
|
0
|
my($x,$y) = ($out->((0)),$out->((1))); |
|
2195
|
0
|
|
|
|
|
0
|
$x .= $k * cos($ph) * sin($th); |
|
2196
|
0
|
|
|
|
|
0
|
$y .= $k * sin($ph); |
|
2197
|
|
|
|
|
|
|
|
|
2198
|
0
|
|
|
|
|
0
|
my $idx = whichND($c > $o->{c}); |
|
2199
|
0
|
0
|
|
|
|
0
|
if($idx->nelem) { |
|
2200
|
0
|
|
|
|
|
0
|
$x->range($idx) .= $o->{bad}; |
|
2201
|
0
|
|
|
|
|
0
|
$y->range($idx) .= $o->{bad}; |
|
2202
|
|
|
|
|
|
|
} |
|
2203
|
|
|
|
|
|
|
|
|
2204
|
0
|
|
|
|
|
0
|
$out; |
|
2205
|
0
|
|
|
|
|
0
|
}; |
|
2206
|
|
|
|
|
|
|
|
|
2207
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
2208
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2209
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2210
|
|
|
|
|
|
|
|
|
2211
|
0
|
|
|
|
|
0
|
my($x,$y) = ($d->((0)),$d->((1))); |
|
2212
|
0
|
|
|
|
|
0
|
my($ph,$th) = ($out->((0)),$out->((1))); |
|
2213
|
0
|
|
|
|
|
0
|
my($rho) = sqrt($x*$x + $y*$y); |
|
2214
|
0
|
|
|
|
|
0
|
my($c) = 2 * asin($rho/2); |
|
2215
|
|
|
|
|
|
|
|
|
2216
|
0
|
|
|
|
|
0
|
$ph .= asin($d->((1)) * sin($c) / $rho); |
|
2217
|
0
|
|
|
|
|
0
|
$th .= atan2($x * sin($c),$rho * cos($c)); |
|
2218
|
|
|
|
|
|
|
|
|
2219
|
0
|
|
|
|
|
0
|
$ph /= $o->{conv}; |
|
2220
|
0
|
|
|
|
|
0
|
$th /= $o->{conv}; |
|
2221
|
|
|
|
|
|
|
|
|
2222
|
0
|
|
|
|
|
0
|
$out; |
|
2223
|
0
|
|
|
|
|
0
|
}; |
|
2224
|
|
|
|
|
|
|
|
|
2225
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
2226
|
|
|
|
|
|
|
} |
|
2227
|
|
|
|
|
|
|
|
|
2228
|
|
|
|
|
|
|
|
|
2229
|
|
|
|
|
|
|
###################################################################### |
|
2230
|
|
|
|
|
|
|
|
|
2231
|
|
|
|
|
|
|
=head2 t_aitoff |
|
2232
|
|
|
|
|
|
|
|
|
2233
|
|
|
|
|
|
|
C in an alias for C |
|
2234
|
|
|
|
|
|
|
|
|
2235
|
|
|
|
|
|
|
=head2 t_hammer |
|
2236
|
|
|
|
|
|
|
|
|
2237
|
|
|
|
|
|
|
=for ref |
|
2238
|
|
|
|
|
|
|
|
|
2239
|
|
|
|
|
|
|
(Cartography) Hammer/Aitoff elliptical projection (az.; auth.) |
|
2240
|
|
|
|
|
|
|
|
|
2241
|
|
|
|
|
|
|
The Hammer/Aitoff projection is often used to display the Celestial |
|
2242
|
|
|
|
|
|
|
sphere. It is mathematically related to the Lambert Azimuthal Equal-Area |
|
2243
|
|
|
|
|
|
|
projection (L), and maps the sphere to an ellipse of unit |
|
2244
|
|
|
|
|
|
|
eccentricity, with vertical radius sqrt(2) and horizontal radius of |
|
2245
|
|
|
|
|
|
|
2 sqrt(2). |
|
2246
|
|
|
|
|
|
|
|
|
2247
|
|
|
|
|
|
|
OPTIONS |
|
2248
|
|
|
|
|
|
|
|
|
2249
|
|
|
|
|
|
|
=over 3 |
|
2250
|
|
|
|
|
|
|
|
|
2251
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
2252
|
|
|
|
|
|
|
|
|
2253
|
|
|
|
|
|
|
=back |
|
2254
|
|
|
|
|
|
|
|
|
2255
|
|
|
|
|
|
|
=cut |
|
2256
|
|
|
|
|
|
|
|
|
2257
|
|
|
|
|
|
|
*t_aitoff = \&t_hammer; |
|
2258
|
|
|
|
|
|
|
|
|
2259
|
|
|
|
|
|
|
sub t_hammer { |
|
2260
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,"Hammer/Aitoff Projection"); |
|
2261
|
|
|
|
|
|
|
|
|
2262
|
0
|
|
|
|
|
0
|
$me->{otype} = ['Longitude','Latitude']; |
|
2263
|
0
|
|
|
|
|
0
|
$me->{ounit} = [' ',' ']; |
|
2264
|
0
|
|
|
|
|
0
|
$me->{odim} = 2; |
|
2265
|
0
|
|
|
|
|
0
|
$me->{idim} = 2; |
|
2266
|
|
|
|
|
|
|
|
|
2267
|
|
|
|
|
|
|
$me->{func} = sub { |
|
2268
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2269
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2270
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $o->{conv}; |
|
2271
|
0
|
|
|
|
|
0
|
my($th) = $out->((0)); |
|
2272
|
0
|
|
|
|
|
0
|
my($ph) = $out->((1)); |
|
2273
|
0
|
|
|
|
|
0
|
my($t) = sqrt( 2 / (1 + cos($ph) * cos($th/2))); |
|
2274
|
0
|
|
|
|
|
0
|
$th .= 2 * $t * cos($ph) * sin($th/2); |
|
2275
|
0
|
|
|
|
|
0
|
$ph .= $t * sin($ph); |
|
2276
|
0
|
|
|
|
|
0
|
$out; |
|
2277
|
|
|
|
|
|
|
} |
|
2278
|
0
|
|
|
|
|
0
|
; |
|
2279
|
|
|
|
|
|
|
|
|
2280
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
2281
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2282
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2283
|
0
|
|
|
|
|
0
|
my($x) = $out->((0)); |
|
2284
|
0
|
|
|
|
|
0
|
my($y) = $out->((1)); |
|
2285
|
|
|
|
|
|
|
|
|
2286
|
0
|
|
|
|
|
0
|
my($rej) = which(($x*$x/8 + $y*$y/2)->flat > 1); |
|
2287
|
|
|
|
|
|
|
|
|
2288
|
0
|
|
|
|
|
0
|
my($zz); |
|
2289
|
0
|
|
|
|
|
0
|
my($z) = sqrt( $zz = (1 - $x*$x/16 - $y*$y/4) ); |
|
2290
|
0
|
|
|
|
|
0
|
$x .= 2 * atan( ($z * $x) / (4 * $zz - 2) ); |
|
2291
|
0
|
|
|
|
|
0
|
$y .= asin($y * $z); |
|
2292
|
|
|
|
|
|
|
|
|
2293
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
2294
|
|
|
|
|
|
|
|
|
2295
|
0
|
|
|
|
|
0
|
$x->flat->($rej) .= $o->{bad}; |
|
2296
|
0
|
|
|
|
|
0
|
$y->flat->($rej) .= $o->{bad}; |
|
2297
|
|
|
|
|
|
|
|
|
2298
|
0
|
|
|
|
|
0
|
$out; |
|
2299
|
0
|
|
|
|
|
0
|
}; |
|
2300
|
|
|
|
|
|
|
|
|
2301
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
2302
|
|
|
|
|
|
|
} |
|
2303
|
|
|
|
|
|
|
|
|
2304
|
|
|
|
|
|
|
|
|
2305
|
|
|
|
|
|
|
###################################################################### |
|
2306
|
|
|
|
|
|
|
|
|
2307
|
|
|
|
|
|
|
=head2 t_zenithal |
|
2308
|
|
|
|
|
|
|
|
|
2309
|
|
|
|
|
|
|
Vertical projections are also called "zenithal", and C is an |
|
2310
|
|
|
|
|
|
|
alias for C. |
|
2311
|
|
|
|
|
|
|
|
|
2312
|
|
|
|
|
|
|
=head2 t_vertical |
|
2313
|
|
|
|
|
|
|
|
|
2314
|
|
|
|
|
|
|
=for usage |
|
2315
|
|
|
|
|
|
|
|
|
2316
|
|
|
|
|
|
|
$t = t_vertical(); |
|
2317
|
|
|
|
|
|
|
|
|
2318
|
|
|
|
|
|
|
=for ref |
|
2319
|
|
|
|
|
|
|
|
|
2320
|
|
|
|
|
|
|
(Cartography) Vertical perspective projection (az.; persp.) |
|
2321
|
|
|
|
|
|
|
|
|
2322
|
|
|
|
|
|
|
Vertical perspective projection is a generalization of L |
|
2323
|
|
|
|
|
|
|
and L projection, and a special case of |
|
2324
|
|
|
|
|
|
|
L projection. It is a projection from the |
|
2325
|
|
|
|
|
|
|
sphere onto a tangent plane from a point at the camera location. |
|
2326
|
|
|
|
|
|
|
|
|
2327
|
|
|
|
|
|
|
OPTIONS |
|
2328
|
|
|
|
|
|
|
|
|
2329
|
|
|
|
|
|
|
=over 3 |
|
2330
|
|
|
|
|
|
|
|
|
2331
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
2332
|
|
|
|
|
|
|
|
|
2333
|
|
|
|
|
|
|
=item m, mask, Mask, h, hemisphere, Hemisphere [default 'near'] |
|
2334
|
|
|
|
|
|
|
|
|
2335
|
|
|
|
|
|
|
The hemisphere to keep in the projection (see L). |
|
2336
|
|
|
|
|
|
|
|
|
2337
|
|
|
|
|
|
|
=item r0, R0, radius, d, dist, distance [default 2.0] |
|
2338
|
|
|
|
|
|
|
|
|
2339
|
|
|
|
|
|
|
The altitude of the focal plane above the center of the sphere. The default |
|
2340
|
|
|
|
|
|
|
places the point of view one radius above the surface. |
|
2341
|
|
|
|
|
|
|
|
|
2342
|
|
|
|
|
|
|
=item t, telescope, Telescope, cam, Camera (default '') |
|
2343
|
|
|
|
|
|
|
|
|
2344
|
|
|
|
|
|
|
If this is set, then the central scale is in telescope or camera |
|
2345
|
|
|
|
|
|
|
angular units rather than in planetary radii. The angular units are |
|
2346
|
|
|
|
|
|
|
parsed as with the normal 'u' option for the lon/lat specification. |
|
2347
|
|
|
|
|
|
|
If you specify a non-string value (such as 1) then you get telescope-frame |
|
2348
|
|
|
|
|
|
|
radians, suitable for working on with other transformations. |
|
2349
|
|
|
|
|
|
|
|
|
2350
|
|
|
|
|
|
|
=item f, fish, fisheye (default '') |
|
2351
|
|
|
|
|
|
|
|
|
2352
|
|
|
|
|
|
|
If this is set then the output is in azimuthal equidistant coordinates |
|
2353
|
|
|
|
|
|
|
instead of in tangent-plane coordinates. This is a convenience function |
|
2354
|
|
|
|
|
|
|
for '(t_az_eqd) x !(t_gnomonic) x (t_vertical)'. |
|
2355
|
|
|
|
|
|
|
|
|
2356
|
|
|
|
|
|
|
=back |
|
2357
|
|
|
|
|
|
|
|
|
2358
|
|
|
|
|
|
|
=cut |
|
2359
|
|
|
|
|
|
|
|
|
2360
|
|
|
|
|
|
|
sub t_vertical { |
|
2361
|
0
|
|
|
0
|
1
|
0
|
my($me) = _new(@_,'Vertical Perspective'); |
|
2362
|
0
|
|
|
|
|
0
|
my $p = $me->{params}; |
|
2363
|
|
|
|
|
|
|
|
|
2364
|
|
|
|
|
|
|
my $m= _opt($me->{options}, |
|
2365
|
0
|
|
|
|
|
0
|
['m','mask','Mask','h','hemi','hemisphere','Hemisphere'], |
|
2366
|
|
|
|
|
|
|
1); |
|
2367
|
|
|
|
|
|
|
|
|
2368
|
0
|
|
|
|
|
0
|
$me->{otype} = ['Perspective X','Perspective Y']; |
|
2369
|
0
|
|
|
|
|
0
|
$me->{ounit} = ['Body radii','Body radii']; |
|
2370
|
|
|
|
|
|
|
|
|
2371
|
0
|
0
|
|
|
|
0
|
if($m=~m/^b/i) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2372
|
0
|
|
|
|
|
0
|
$p->{m} = 0; |
|
2373
|
|
|
|
|
|
|
} elsif($m=~m/^n/i) { |
|
2374
|
0
|
|
|
|
|
0
|
$p->{m} = 1; |
|
2375
|
|
|
|
|
|
|
} elsif($m=~m/^f/i) { |
|
2376
|
0
|
|
|
|
|
0
|
$p->{m} = 2; |
|
2377
|
|
|
|
|
|
|
} else { |
|
2378
|
0
|
|
|
|
|
0
|
$p->{m} = $m; |
|
2379
|
|
|
|
|
|
|
} |
|
2380
|
|
|
|
|
|
|
|
|
2381
|
|
|
|
|
|
|
$p->{r0} = _opt($me->{options}, |
|
2382
|
0
|
|
|
|
|
0
|
['r0','R0','radius','Radius', |
|
2383
|
|
|
|
|
|
|
'd','dist','distance','Distance'], |
|
2384
|
|
|
|
|
|
|
2.0 |
|
2385
|
|
|
|
|
|
|
); |
|
2386
|
|
|
|
|
|
|
|
|
2387
|
0
|
0
|
|
|
|
0
|
if($p->{r0} == 0) { |
|
2388
|
0
|
0
|
|
|
|
0
|
print "t_vertical: r0 = 0; using t_gnomonic instead\n" |
|
2389
|
|
|
|
|
|
|
if($PDLA::verbose); |
|
2390
|
0
|
|
|
|
|
0
|
return t_gnomonic($me->{options}); |
|
2391
|
|
|
|
|
|
|
} |
|
2392
|
|
|
|
|
|
|
|
|
2393
|
0
|
0
|
|
|
|
0
|
if($p->{r0} == 1) { |
|
2394
|
0
|
0
|
|
|
|
0
|
print "t_vertical: r0 = 1; using t_stereographic instead\n" |
|
2395
|
|
|
|
|
|
|
if($PDLA::verbose); |
|
2396
|
0
|
|
|
|
|
0
|
return t_stereographic($me->{options}); |
|
2397
|
|
|
|
|
|
|
} |
|
2398
|
|
|
|
|
|
|
|
|
2399
|
|
|
|
|
|
|
|
|
2400
|
|
|
|
|
|
|
$p->{t} = _opt($me->{options}, |
|
2401
|
0
|
|
|
|
|
0
|
['t','tele','telescope','Telescope', |
|
2402
|
|
|
|
|
|
|
'cam','camera','Camera'], |
|
2403
|
|
|
|
|
|
|
undef); |
|
2404
|
|
|
|
|
|
|
|
|
2405
|
|
|
|
|
|
|
$p->{f} = _opt($me->{options}, |
|
2406
|
0
|
|
|
|
|
0
|
['f','fish','fisheye','Fisheye'], |
|
2407
|
|
|
|
|
|
|
undef); |
|
2408
|
|
|
|
|
|
|
|
|
2409
|
|
|
|
|
|
|
$p->{t} = 'rad' |
|
2410
|
0
|
0
|
0
|
|
|
0
|
if($p->{f} && !defined($p->{t})); |
|
2411
|
|
|
|
|
|
|
|
|
2412
|
|
|
|
|
|
|
$p->{tconv} = _uconv($p->{t},1) || _uconv('rad') |
|
2413
|
0
|
0
|
0
|
|
|
0
|
if(defined $p->{t}); |
|
2414
|
|
|
|
|
|
|
|
|
2415
|
|
|
|
|
|
|
$me->{func} = sub { |
|
2416
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2417
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2418
|
0
|
|
|
|
|
0
|
my($th) = $d->((0))*$o->{conv}; |
|
2419
|
0
|
|
|
|
|
0
|
my($ph) = $d->((1))*$o->{conv}; |
|
2420
|
|
|
|
|
|
|
|
|
2421
|
0
|
|
|
|
|
0
|
my($cph) = cos($ph); |
|
2422
|
|
|
|
|
|
|
|
|
2423
|
0
|
|
|
|
|
0
|
my($cos_c) = $cph * cos($th); |
|
2424
|
|
|
|
|
|
|
|
|
2425
|
|
|
|
|
|
|
my($k) = (($o->{r0} - 1) / |
|
2426
|
0
|
|
|
|
|
0
|
($o->{r0} - $cos_c)); |
|
2427
|
|
|
|
|
|
|
|
|
2428
|
|
|
|
|
|
|
# If it's a telescope perspective, figure the apparent size |
|
2429
|
|
|
|
|
|
|
# of the globe and scale accordingly. |
|
2430
|
0
|
0
|
|
|
|
0
|
if($o->{t}) { |
|
2431
|
0
|
|
|
|
|
0
|
my($theta) = asin(1/$o->{r0}); |
|
2432
|
|
|
|
|
|
|
} |
|
2433
|
|
|
|
|
|
|
|
|
2434
|
|
|
|
|
|
|
$out->(0:1) /= ($o->{r0} - 1.0) * ($o->{f} ? 1.0 : $o->{tconv}) |
|
2435
|
0
|
0
|
|
|
|
0
|
if($o->{t}); |
|
|
|
0
|
|
|
|
|
|
|
2436
|
|
|
|
|
|
|
|
|
2437
|
|
|
|
|
|
|
|
|
2438
|
|
|
|
|
|
|
|
|
2439
|
0
|
|
|
|
|
0
|
$out->((0)) .= $cph * sin($th); |
|
2440
|
0
|
|
|
|
|
0
|
$out->((1)) .= sin($ph); |
|
2441
|
|
|
|
|
|
|
|
|
2442
|
|
|
|
|
|
|
# Handle singularity at the origin |
|
2443
|
0
|
|
|
|
|
0
|
$k->where(($out->((0)) == 0) & ($out->((1)) == 0)) .= 0; |
|
2444
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $k->dummy(0,2); |
|
2445
|
|
|
|
|
|
|
|
|
2446
|
0
|
0
|
|
|
|
0
|
if($o->{m}) { |
|
2447
|
0
|
|
|
|
|
0
|
my $idx; |
|
2448
|
|
|
|
|
|
|
$idx = whichND($cos_c < 1.0/$o->{r0}) |
|
2449
|
0
|
0
|
|
|
|
0
|
if($o->{m} == 1); |
|
2450
|
|
|
|
|
|
|
$idx = whichND($cos_c > 1.0/$o->{r0}) |
|
2451
|
0
|
0
|
|
|
|
0
|
if($o->{m} == 2); |
|
2452
|
|
|
|
|
|
|
|
|
2453
|
0
|
0
|
0
|
|
|
0
|
if(defined $idx && ref $idx eq 'PDLA' && $idx->nelem){ |
|
|
|
|
0
|
|
|
|
|
|
2454
|
0
|
|
|
|
|
0
|
$out->((0))->range($idx) .= $o->{bad}; |
|
2455
|
0
|
|
|
|
|
0
|
$out->((1))->range($idx) .= $o->{bad}; |
|
2456
|
|
|
|
|
|
|
} |
|
2457
|
|
|
|
|
|
|
} |
|
2458
|
|
|
|
|
|
|
|
|
2459
|
|
|
|
|
|
|
|
|
2460
|
0
|
|
|
|
|
0
|
$out; |
|
2461
|
0
|
|
|
|
|
0
|
}; |
|
2462
|
|
|
|
|
|
|
|
|
2463
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
2464
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2465
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2466
|
|
|
|
|
|
|
|
|
2467
|
|
|
|
|
|
|
# Reverse the hemisphere if the mask is set to 'far' |
|
2468
|
0
|
0
|
|
|
|
0
|
my($P) = ($o->{m} == 2) ? -$o->{r0} : $o->{r0}; |
|
2469
|
|
|
|
|
|
|
|
|
2470
|
|
|
|
|
|
|
$out->(0:1) *= ($P - 1.0) * ($o->{f} ? 1.0 : $o->{tconv}) |
|
2471
|
0
|
0
|
|
|
|
0
|
if($o->{t}); |
|
|
|
0
|
|
|
|
|
|
|
2472
|
|
|
|
|
|
|
|
|
2473
|
0
|
|
|
|
|
0
|
my($rho) = sqrt(sumover($d->(0:1) * $d->(0:1))); |
|
2474
|
0
|
|
|
|
|
0
|
my($sin_c) = ( ( $P - sqrt( 1 - ($rho*$rho * ($P+1)/($P-1)) ) ) / |
|
2475
|
|
|
|
|
|
|
( ($P-1)/$rho + $rho/($P-1) ) |
|
2476
|
|
|
|
|
|
|
); |
|
2477
|
|
|
|
|
|
|
|
|
2478
|
0
|
|
|
|
|
0
|
my($cos_c) = sqrt(1 - $sin_c*$sin_c); |
|
2479
|
|
|
|
|
|
|
|
|
2480
|
|
|
|
|
|
|
# Switch c's quadrant where necessary, by inverting cos(c). |
|
2481
|
0
|
0
|
|
|
|
0
|
if($P<0) { |
|
2482
|
0
|
|
|
|
|
0
|
my $idx = whichND($rho > ($P-1/$P)); |
|
2483
|
0
|
0
|
|
|
|
0
|
$cos_c->range($idx) *= -1 |
|
2484
|
|
|
|
|
|
|
if($idx->nelem > 0); |
|
2485
|
|
|
|
|
|
|
} |
|
2486
|
|
|
|
|
|
|
|
|
2487
|
|
|
|
|
|
|
|
|
2488
|
0
|
|
|
|
|
0
|
$out->((0)) .= atan( $d->((0)) * $sin_c / ($rho * $cos_c) ); |
|
2489
|
0
|
|
|
|
|
0
|
$out->((1)) .= asin( $d->((1)) * $sin_c / $rho ); |
|
2490
|
|
|
|
|
|
|
|
|
2491
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
2492
|
|
|
|
|
|
|
|
|
2493
|
0
|
|
|
|
|
0
|
$out; |
|
2494
|
0
|
|
|
|
|
0
|
}; |
|
2495
|
|
|
|
|
|
|
|
|
2496
|
|
|
|
|
|
|
|
|
2497
|
|
|
|
|
|
|
# Compose on both front and back as necessary. |
|
2498
|
|
|
|
|
|
|
return t_compose( t_scale(1.0/$p->{tconv}), |
|
2499
|
|
|
|
|
|
|
t_az_eqd, |
|
2500
|
|
|
|
|
|
|
t_gnomonic->inverse, |
|
2501
|
|
|
|
|
|
|
$me->_finish ) |
|
2502
|
0
|
0
|
|
|
|
0
|
if($p->{f}); |
|
2503
|
|
|
|
|
|
|
|
|
2504
|
0
|
|
|
|
|
0
|
$me->_finish; |
|
2505
|
|
|
|
|
|
|
} |
|
2506
|
|
|
|
|
|
|
|
|
2507
|
|
|
|
|
|
|
*t_zenithal = \&t_vertical; |
|
2508
|
|
|
|
|
|
|
|
|
2509
|
|
|
|
|
|
|
###################################################################### |
|
2510
|
|
|
|
|
|
|
|
|
2511
|
|
|
|
|
|
|
=head2 t_perspective |
|
2512
|
|
|
|
|
|
|
|
|
2513
|
|
|
|
|
|
|
=for usage |
|
2514
|
|
|
|
|
|
|
|
|
2515
|
|
|
|
|
|
|
$t = t_perspective(); |
|
2516
|
|
|
|
|
|
|
|
|
2517
|
|
|
|
|
|
|
=for ref |
|
2518
|
|
|
|
|
|
|
|
|
2519
|
|
|
|
|
|
|
(Cartography) Arbitrary perspective projection |
|
2520
|
|
|
|
|
|
|
|
|
2521
|
|
|
|
|
|
|
Perspective projection onto a focal plane from an arbitrary location |
|
2522
|
|
|
|
|
|
|
within or without the sphere, with an arbitrary central look direction, |
|
2523
|
|
|
|
|
|
|
and with correction for magnification within the optical system. |
|
2524
|
|
|
|
|
|
|
|
|
2525
|
|
|
|
|
|
|
In the forward direction, t_perspective generates perspective views of |
|
2526
|
|
|
|
|
|
|
a sphere given (lon/lat) mapping or vector information. In the reverse |
|
2527
|
|
|
|
|
|
|
direction, t_perspective produces (lon/lat) maps from aerial or distant |
|
2528
|
|
|
|
|
|
|
photographs of spherical objects. |
|
2529
|
|
|
|
|
|
|
|
|
2530
|
|
|
|
|
|
|
Viewpoints outside the sphere treat the sphere as opaque by default, |
|
2531
|
|
|
|
|
|
|
though you can use the 'm' option to specify either the near or far |
|
2532
|
|
|
|
|
|
|
surface (relative to the origin). Viewpoints below the surface treat |
|
2533
|
|
|
|
|
|
|
the sphere as transparent and undergo a mirror reversal for |
|
2534
|
|
|
|
|
|
|
consistency with projections that are special cases of the perspective |
|
2535
|
|
|
|
|
|
|
projection (e.g. t_gnomonic for r0=0 or t_stereographic for r0=-1). |
|
2536
|
|
|
|
|
|
|
|
|
2537
|
|
|
|
|
|
|
Magnification correction handles the extra edge distortion due to |
|
2538
|
|
|
|
|
|
|
higher angles between the focal plane and focused rays within the |
|
2539
|
|
|
|
|
|
|
optical system of your camera. If you do not happen to know the |
|
2540
|
|
|
|
|
|
|
magnification of your camera, a simple rule of thumb is that the |
|
2541
|
|
|
|
|
|
|
magnification of a reflective telescope is roughly its focal length |
|
2542
|
|
|
|
|
|
|
(plate scale) divided by its physical length; and the magnification of |
|
2543
|
|
|
|
|
|
|
a compound refractive telescope is roughly twice its physical length divided |
|
2544
|
|
|
|
|
|
|
by its focal length. Simple optical systems with a single optic have |
|
2545
|
|
|
|
|
|
|
magnification = 1. Fisheye lenses have magnification < 1. |
|
2546
|
|
|
|
|
|
|
|
|
2547
|
|
|
|
|
|
|
This transformation was derived by direct geometrical calculation |
|
2548
|
|
|
|
|
|
|
rather than being translated from Voxland & Snyder. |
|
2549
|
|
|
|
|
|
|
|
|
2550
|
|
|
|
|
|
|
OPTIONS |
|
2551
|
|
|
|
|
|
|
|
|
2552
|
|
|
|
|
|
|
=over 3 |
|
2553
|
|
|
|
|
|
|
|
|
2554
|
|
|
|
|
|
|
=item STANDARD POSITIONAL OPTIONS |
|
2555
|
|
|
|
|
|
|
|
|
2556
|
|
|
|
|
|
|
As always, the 'origin' field specifies the sub-camera point on the |
|
2557
|
|
|
|
|
|
|
sphere. |
|
2558
|
|
|
|
|
|
|
|
|
2559
|
|
|
|
|
|
|
The 'roll' option is the roll angle about the sub-camera point, for |
|
2560
|
|
|
|
|
|
|
consistency with the other projectons. |
|
2561
|
|
|
|
|
|
|
|
|
2562
|
|
|
|
|
|
|
=item p, ptg, pointing, Pointing (default (0,0,0)) |
|
2563
|
|
|
|
|
|
|
|
|
2564
|
|
|
|
|
|
|
The pointing direction, in (horiz. offset, vert. offset, roll) of the |
|
2565
|
|
|
|
|
|
|
camera relative to the center of the sphere. This is a spherical |
|
2566
|
|
|
|
|
|
|
coordinate system with the origin pointing directly at the sphere and |
|
2567
|
|
|
|
|
|
|
the pole pointing north in the pre-rolled coordinate system set by the |
|
2568
|
|
|
|
|
|
|
standard origin. It's most useful for space-based images taken some distance |
|
2569
|
|
|
|
|
|
|
from the body in question (e.g. images of other planets or the Sun). |
|
2570
|
|
|
|
|
|
|
|
|
2571
|
|
|
|
|
|
|
Be careful not to confuse 'p' (pointing) with 'P' (P angle, a standard |
|
2572
|
|
|
|
|
|
|
synonym for roll). |
|
2573
|
|
|
|
|
|
|
|
|
2574
|
|
|
|
|
|
|
=item c, cam, camera, Camera (default undef) |
|
2575
|
|
|
|
|
|
|
|
|
2576
|
|
|
|
|
|
|
Alternate way of specifying the camera pointing, using a spherical |
|
2577
|
|
|
|
|
|
|
coordinate system with poles at the zenith (positive) and nadir |
|
2578
|
|
|
|
|
|
|
(negative) -- this is useful for aerial photographs and such, where |
|
2579
|
|
|
|
|
|
|
the point of view is near the surface of the sphere. You specify |
|
2580
|
|
|
|
|
|
|
(azimuth from N, altitude from horizontal, roll from vertical=up). If |
|
2581
|
|
|
|
|
|
|
you specify pointing by this method, it overrides the 'pointing' |
|
2582
|
|
|
|
|
|
|
option, above. This coordinate system is most useful for aerial photography |
|
2583
|
|
|
|
|
|
|
or low-orbit work, where the nadir is not necessarily the most interesting |
|
2584
|
|
|
|
|
|
|
part of the scene. |
|
2585
|
|
|
|
|
|
|
|
|
2586
|
|
|
|
|
|
|
=item r0, R0, radius, d, dist, distance [default 2.0] |
|
2587
|
|
|
|
|
|
|
|
|
2588
|
|
|
|
|
|
|
The altitude of the point of view above the center of the sphere. |
|
2589
|
|
|
|
|
|
|
The default places the point of view 1 radius aboove the surface. |
|
2590
|
|
|
|
|
|
|
Do not confuse this with 'r', the standard origin roll angle! Setting |
|
2591
|
|
|
|
|
|
|
r0 < 1 gives a viewpoint inside the sphere. In that case, the images are |
|
2592
|
|
|
|
|
|
|
mirror-reversed to preserve the chiralty of the perspective. Setting |
|
2593
|
|
|
|
|
|
|
r0=0 gives gnomonic projections; setting r0=-1 gives stereographic projections. |
|
2594
|
|
|
|
|
|
|
Setting r0 < -1 gives strange results. |
|
2595
|
|
|
|
|
|
|
|
|
2596
|
|
|
|
|
|
|
=item iu, im_unit, image_unit, Image_Unit (default 'degrees') |
|
2597
|
|
|
|
|
|
|
|
|
2598
|
|
|
|
|
|
|
This is the angular units in which the viewing camera is calibrated |
|
2599
|
|
|
|
|
|
|
at the center of the image. |
|
2600
|
|
|
|
|
|
|
|
|
2601
|
|
|
|
|
|
|
=item mag, magnification, Magnification (default 1.0) |
|
2602
|
|
|
|
|
|
|
|
|
2603
|
|
|
|
|
|
|
This is the magnification factor applied to the optics -- it affects the |
|
2604
|
|
|
|
|
|
|
amount of tangent-plane distortion within the telescope. |
|
2605
|
|
|
|
|
|
|
1.0 yields the view from a simple optical system; higher values are |
|
2606
|
|
|
|
|
|
|
telescopic, while lower values are wide-angle (fisheye). Higher |
|
2607
|
|
|
|
|
|
|
magnification leads to higher angles within the optical system, and more |
|
2608
|
|
|
|
|
|
|
tangent-plane distortion at the edges of the image. |
|
2609
|
|
|
|
|
|
|
The magnification is applied to the incident angles themselves, rather than |
|
2610
|
|
|
|
|
|
|
to their tangents (simple two-element telescopes magnify tan(theta) rather |
|
2611
|
|
|
|
|
|
|
than theta itself); this is appropriate because wide-field optics more |
|
2612
|
|
|
|
|
|
|
often conform to the equidistant azimuthal approximation than to the |
|
2613
|
|
|
|
|
|
|
tangent plane approximation. If you need more detailed control of |
|
2614
|
|
|
|
|
|
|
the relationship between incident angle and focal-plane position, |
|
2615
|
|
|
|
|
|
|
use mag=1.0 and compose the transform with something else to tweak the |
|
2616
|
|
|
|
|
|
|
angles. |
|
2617
|
|
|
|
|
|
|
|
|
2618
|
|
|
|
|
|
|
=item m, mask, Mask, h, hemisphere, Hemisphere [default 'near'] |
|
2619
|
|
|
|
|
|
|
|
|
2620
|
|
|
|
|
|
|
'hemisphere' is by analogy to other cartography methods although the two |
|
2621
|
|
|
|
|
|
|
regions to be selected are not really hemispheres. |
|
2622
|
|
|
|
|
|
|
|
|
2623
|
|
|
|
|
|
|
=item f, fov, field_of_view, Field_Of_View [default 60 degrees] |
|
2624
|
|
|
|
|
|
|
|
|
2625
|
|
|
|
|
|
|
The field of view of the telescope -- sets the crop radius on the |
|
2626
|
|
|
|
|
|
|
focal plane. If you pass in a scalar, you get a circular crop. If you |
|
2627
|
|
|
|
|
|
|
pass in a 2-element list ref, you get a rectilinear crop, with the |
|
2628
|
|
|
|
|
|
|
horizontal 'radius' and vertical 'radius' set separately. |
|
2629
|
|
|
|
|
|
|
|
|
2630
|
|
|
|
|
|
|
=back |
|
2631
|
|
|
|
|
|
|
|
|
2632
|
|
|
|
|
|
|
EXAMPLES |
|
2633
|
|
|
|
|
|
|
|
|
2634
|
|
|
|
|
|
|
Model a camera looking at the Sun through a 10x telescope from Earth |
|
2635
|
|
|
|
|
|
|
(~230 solar radii from the Sun), with an 0.5 degree field of view and |
|
2636
|
|
|
|
|
|
|
a solar P (roll) angle of 30 degrees, in February (sub-Earth solar |
|
2637
|
|
|
|
|
|
|
latitude is 7 degrees south). Convert a solar FITS image taken with |
|
2638
|
|
|
|
|
|
|
that camera to a FITS lon/lat map of the Sun with 20 pixels/degree |
|
2639
|
|
|
|
|
|
|
latitude: |
|
2640
|
|
|
|
|
|
|
|
|
2641
|
|
|
|
|
|
|
# Define map output header (no need if you don't want a FITS output map) |
|
2642
|
|
|
|
|
|
|
$maphdr = {NAXIS1=>7200,NAXIS2=>3600, # Size of image |
|
2643
|
|
|
|
|
|
|
CTYPE1=>longitude,CTYPE2=>latitude, # Type of axes |
|
2644
|
|
|
|
|
|
|
CUNIT1=>deg,CUNIT2=>deg, # Unit of axes |
|
2645
|
|
|
|
|
|
|
CDELT1=>0.05,CDELT2=>0.05, # Scale of axes |
|
2646
|
|
|
|
|
|
|
CRPIX1=>3601,CRPIX2=>1801, # Center of map |
|
2647
|
|
|
|
|
|
|
CRVAL1=>0,CRVAL2=>0 # (lon,lat) of center |
|
2648
|
|
|
|
|
|
|
}; |
|
2649
|
|
|
|
|
|
|
|
|
2650
|
|
|
|
|
|
|
# Set up the perspective transformation, and apply it. |
|
2651
|
|
|
|
|
|
|
$t = t_perspective(r0=>229,fov=>0.5,mag=>10,P=>30,B=>-7); |
|
2652
|
|
|
|
|
|
|
$map = $im->map( $t , $maphdr ); |
|
2653
|
|
|
|
|
|
|
|
|
2654
|
|
|
|
|
|
|
Draw an aerial-view map of the Chesapeake Bay, as seen from a sounding |
|
2655
|
|
|
|
|
|
|
rocket at an altitude of 100km, looking NNE from ~200km south of |
|
2656
|
|
|
|
|
|
|
Washington (the radius of Earth is 6378 km; Washington D.C. is at |
|
2657
|
|
|
|
|
|
|
roughly 77W,38N). Superimpose a linear coastline map on a photographic map. |
|
2658
|
|
|
|
|
|
|
|
|
2659
|
|
|
|
|
|
|
$x = graticule(1,0.1)->glue(1,earth_coast()); |
|
2660
|
|
|
|
|
|
|
$t = t_perspective(r0=>6478/6378.0,fov=>60,cam=>[22.5,-20],o=>[-77,36]) |
|
2661
|
|
|
|
|
|
|
$w = pgwin(size=>[10,6],J=>1); |
|
2662
|
|
|
|
|
|
|
$w->fits_imag(earth_image()->map($t,[800,500],{m=>linear})); |
|
2663
|
|
|
|
|
|
|
$w->hold; |
|
2664
|
|
|
|
|
|
|
$w->lines($x->apply($t),{xt=>'Degrees',yt=>'Degrees'}); |
|
2665
|
|
|
|
|
|
|
$w->release; |
|
2666
|
|
|
|
|
|
|
|
|
2667
|
|
|
|
|
|
|
Model a 5x telescope looking at Betelgeuse with a 10 degree field of view |
|
2668
|
|
|
|
|
|
|
(since the telescope is looking at the Celestial sphere, r is 0 and this |
|
2669
|
|
|
|
|
|
|
is just an expensive modified-gnomonic projection). |
|
2670
|
|
|
|
|
|
|
|
|
2671
|
|
|
|
|
|
|
$t = t_perspective(r0=>0,fov=>10,mag=>5,o=>[88.79,7.41]) |
|
2672
|
|
|
|
|
|
|
|
|
2673
|
|
|
|
|
|
|
=cut |
|
2674
|
|
|
|
|
|
|
|
|
2675
|
|
|
|
|
|
|
sub t_perspective { |
|
2676
|
1
|
|
|
1
|
1
|
3210
|
my($me) = _new(@_,'Focal-Plane Perspective'); |
|
2677
|
1
|
|
|
|
|
5
|
my $p = $me->{params}; |
|
2678
|
|
|
|
|
|
|
|
|
2679
|
|
|
|
|
|
|
my $m= _opt($me->{options}, |
|
2680
|
1
|
|
|
|
|
8
|
['m','mask','Mask','h','hemi','hemisphere','Hemisphere'], |
|
2681
|
|
|
|
|
|
|
1); |
|
2682
|
1
|
|
|
|
|
4
|
$p->{m} = $m; |
|
2683
|
1
|
50
|
|
|
|
5
|
$p->{m} = 0 if($m=~m/^b/i); |
|
2684
|
1
|
50
|
|
|
|
12
|
$p->{m} = 1 if($m=~m/^n/i); |
|
2685
|
1
|
50
|
|
|
|
4
|
$p->{m} = 2 if($m=~m/^f/i); |
|
2686
|
|
|
|
|
|
|
|
|
2687
|
|
|
|
|
|
|
$p->{r0} = _opt($me->{options}, |
|
2688
|
1
|
|
|
|
|
5
|
['r0','R0','radius','Radius', |
|
2689
|
|
|
|
|
|
|
'd','dist','distance','Distance'], |
|
2690
|
|
|
|
|
|
|
2.0 |
|
2691
|
|
|
|
|
|
|
); |
|
2692
|
|
|
|
|
|
|
|
|
2693
|
|
|
|
|
|
|
$p->{iu} = _opt($me->{options}, |
|
2694
|
1
|
|
|
|
|
5
|
['i','iu','image_unit','Image_Unit'], |
|
2695
|
|
|
|
|
|
|
'degrees'); |
|
2696
|
|
|
|
|
|
|
|
|
2697
|
1
|
|
|
|
|
3
|
$p->{tconv} = _uconv($p->{iu}); |
|
2698
|
|
|
|
|
|
|
|
|
2699
|
|
|
|
|
|
|
$p->{mag} = _opt($me->{options}, |
|
2700
|
1
|
|
|
|
|
5
|
['mag','magnification','Magnification'], |
|
2701
|
|
|
|
|
|
|
1.0); |
|
2702
|
|
|
|
|
|
|
|
|
2703
|
|
|
|
|
|
|
# Regular pointing pseudovector -- make sure there are exactly 3 elements |
|
2704
|
|
|
|
|
|
|
$p->{p} = (topdl(_opt($me->{options}, |
|
2705
|
|
|
|
|
|
|
['p','ptg','pointing','Pointing'], |
|
2706
|
|
|
|
|
|
|
[0,0,0]) |
|
2707
|
|
|
|
|
|
|
) |
|
2708
|
|
|
|
|
|
|
* $p->{tconv} |
|
2709
|
1
|
|
|
|
|
5
|
)->append(zeroes(3))->(0:2); |
|
2710
|
|
|
|
|
|
|
|
|
2711
|
1
|
|
|
|
|
143
|
$p->{pmat} = _rotmat( (- $p->{p})->list ); |
|
2712
|
|
|
|
|
|
|
|
|
2713
|
|
|
|
|
|
|
# Funky camera pointing pseudovector overrides normal pointing option |
|
2714
|
|
|
|
|
|
|
$p->{c} = _opt($me->{options}, |
|
2715
|
1
|
|
|
|
|
189
|
['c','cam','camera','Camera'], |
|
2716
|
|
|
|
|
|
|
undef |
|
2717
|
|
|
|
|
|
|
); |
|
2718
|
|
|
|
|
|
|
|
|
2719
|
1
|
50
|
|
|
|
6
|
if(defined($p->{c})) { |
|
2720
|
0
|
|
|
|
|
0
|
$p->{c} = (topdl($p->{c}) * $p->{tconv})->append(zeroes(3))->(0:2); |
|
2721
|
|
|
|
|
|
|
|
|
2722
|
|
|
|
|
|
|
$p->{pmat} = ( _rotmat( 0,-$PI/2,0 ) x |
|
2723
|
0
|
|
|
|
|
0
|
_rotmat( (-$p->{c})->list ) |
|
2724
|
|
|
|
|
|
|
); |
|
2725
|
|
|
|
|
|
|
} |
|
2726
|
|
|
|
|
|
|
|
|
2727
|
|
|
|
|
|
|
# Reflect X axis if we're inside the sphere. |
|
2728
|
1
|
50
|
|
|
|
3
|
if($p->{r0}<1) { |
|
2729
|
0
|
|
|
|
|
0
|
$p->{pmat} = topdl([[-1,0,0],[0,1,0],[0,0,1]]) x $p->{pmat}; |
|
2730
|
|
|
|
|
|
|
} |
|
2731
|
|
|
|
|
|
|
|
|
2732
|
|
|
|
|
|
|
$p->{f} = ( _opt($me->{options}, |
|
2733
|
|
|
|
|
|
|
['f','fov','field_of_view','Field_of_View'], |
|
2734
|
|
|
|
|
|
|
topdl($PI*2/3) / $p->{tconv} / $p->{mag} ) |
|
2735
|
|
|
|
|
|
|
* $p->{tconv} |
|
2736
|
1
|
|
|
|
|
7
|
); |
|
2737
|
|
|
|
|
|
|
|
|
2738
|
1
|
|
|
|
|
20
|
$me->{otype} = ['Tan X','Tan Y']; |
|
2739
|
1
|
|
|
|
|
4
|
$me->{ounit} = [$p->{iu},$p->{iu}]; |
|
2740
|
|
|
|
|
|
|
|
|
2741
|
|
|
|
|
|
|
# "Prefilter" -- subsidiary transform to convert the |
|
2742
|
|
|
|
|
|
|
# spherical coordinates to 3-D coords in the viewer's |
|
2743
|
|
|
|
|
|
|
# reference frame (Y,Z are more-or-less tangent-plane X and Y, |
|
2744
|
|
|
|
|
|
|
# and -X is the direction toward the planet, before rotation |
|
2745
|
|
|
|
|
|
|
# to account for pointing). |
|
2746
|
|
|
|
|
|
|
|
|
2747
|
|
|
|
|
|
|
$me->{params}->{prefilt} = |
|
2748
|
|
|
|
|
|
|
t_compose( |
|
2749
|
|
|
|
|
|
|
# Offset for the camera pointing. |
|
2750
|
|
|
|
|
|
|
t_linear(m=>$p->{pmat}, |
|
2751
|
|
|
|
|
|
|
d=>3), |
|
2752
|
|
|
|
|
|
|
|
|
2753
|
|
|
|
|
|
|
# Rotate the sphere so the correct origin is at the |
|
2754
|
|
|
|
|
|
|
# maximum-X point, then move the whole thing in the |
|
2755
|
|
|
|
|
|
|
# -X direction by r0. |
|
2756
|
|
|
|
|
|
|
t_linear(m=>(_rotmat($p->{o}->at(0), |
|
2757
|
|
|
|
|
|
|
$p->{o}->at(1), |
|
2758
|
|
|
|
|
|
|
$p->{roll}->at(0)) |
|
2759
|
|
|
|
|
|
|
), |
|
2760
|
|
|
|
|
|
|
d=>3, |
|
2761
|
1
|
|
|
|
|
5
|
post=> topdl( [- $me->{params}->{r0},0,0] ) |
|
2762
|
|
|
|
|
|
|
), |
|
2763
|
|
|
|
|
|
|
|
|
2764
|
|
|
|
|
|
|
# Put initial sci. coords into Cartesian space |
|
2765
|
|
|
|
|
|
|
t_unit_sphere(u=>'radian') |
|
2766
|
|
|
|
|
|
|
); |
|
2767
|
|
|
|
|
|
|
|
|
2768
|
|
|
|
|
|
|
# Store the origin of the sphere -- useful for the inverse function |
|
2769
|
|
|
|
|
|
|
$me->{params}->{sph_origin} = ( |
|
2770
|
|
|
|
|
|
|
topdl([-$me->{params}->{r0},0,0]) x |
|
2771
|
|
|
|
|
|
|
$p->{pmat} |
|
2772
|
1
|
|
|
|
|
13
|
)->(:,(0)); |
|
2773
|
|
|
|
|
|
|
|
|
2774
|
|
|
|
|
|
|
# |
|
2775
|
|
|
|
|
|
|
# Finally, the meat -- the forward function! |
|
2776
|
|
|
|
|
|
|
# |
|
2777
|
|
|
|
|
|
|
$me->{func} = sub { |
|
2778
|
8
|
|
|
8
|
|
24
|
my($d,$o) = @_; |
|
2779
|
|
|
|
|
|
|
|
|
2780
|
8
|
50
|
|
|
|
47
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2781
|
8
|
|
|
|
|
45463
|
$out->(0:1) *= $o->{conv}; |
|
2782
|
|
|
|
|
|
|
|
|
2783
|
|
|
|
|
|
|
# If we're outside the sphere, do hemisphere filtering |
|
2784
|
8
|
|
|
|
|
5675
|
my $idx; |
|
2785
|
8
|
50
|
|
|
|
48
|
if(abs($o->{r0}) < 1 ) { |
|
2786
|
0
|
|
|
|
|
0
|
$idx = null; |
|
2787
|
|
|
|
|
|
|
} else { |
|
2788
|
|
|
|
|
|
|
# Great-circle distance to origin |
|
2789
|
|
|
|
|
|
|
my($cos_c) = ( sin($o->{o}->((1))) * sin($out->((1))) |
|
2790
|
|
|
|
|
|
|
+ |
|
2791
|
|
|
|
|
|
|
cos($o->{o}->((1))) * cos($out->((1))) * |
|
2792
|
8
|
|
|
|
|
48
|
cos($out->((0)) - $o->{o}->((0))) |
|
2793
|
|
|
|
|
|
|
); |
|
2794
|
|
|
|
|
|
|
|
|
2795
|
8
|
|
|
|
|
127528
|
my($thresh) = (1.0/$o->{r0}); |
|
2796
|
|
|
|
|
|
|
|
|
2797
|
8
|
50
|
|
|
|
58
|
if($o->{m}==1) { |
|
|
|
0
|
|
|
|
|
|
|
2798
|
8
|
|
|
|
|
2564
|
$idx = whichND($cos_c < $thresh); |
|
2799
|
|
|
|
|
|
|
} elsif($o->{m}==2) { |
|
2800
|
0
|
|
|
|
|
0
|
$idx = whichND($cos_c > $thresh); |
|
2801
|
|
|
|
|
|
|
} else { |
|
2802
|
0
|
|
|
|
|
0
|
$idx = null; |
|
2803
|
|
|
|
|
|
|
} |
|
2804
|
|
|
|
|
|
|
} |
|
2805
|
|
|
|
|
|
|
|
|
2806
|
|
|
|
|
|
|
### Transform everything -- just chuck out the bad points at the end. |
|
2807
|
|
|
|
|
|
|
|
|
2808
|
|
|
|
|
|
|
## convert to 3-D viewer coordinates (there's a dimension change!) |
|
2809
|
8
|
|
|
|
|
87371
|
my $dc = $out->apply($o->{prefilt}); |
|
2810
|
|
|
|
|
|
|
|
|
2811
|
|
|
|
|
|
|
## Apply the tangent-plane transform, and scale by the magnification. |
|
2812
|
8
|
|
|
|
|
88
|
my $dcyz = $dc->(1:2); |
|
2813
|
|
|
|
|
|
|
|
|
2814
|
8
|
|
|
|
|
29242
|
my $r = ( $dcyz * $dcyz ) -> sumover -> sqrt ; |
|
2815
|
8
|
|
|
|
|
100
|
my $rscale; |
|
2816
|
8
|
50
|
|
|
|
55
|
if( $o->{mag} == 1.0 ) { |
|
2817
|
8
|
|
|
|
|
59
|
$rscale = - 1.0 / $dc->((0)); |
|
2818
|
|
|
|
|
|
|
} else { |
|
2819
|
0
|
0
|
|
|
|
0
|
print "(using magnification...)\n" if $PDLA::verbose; |
|
2820
|
0
|
|
|
|
|
0
|
$rscale = - tan( $o->{mag} * atan( $r / $dc->((0)) ) ) / $r; |
|
2821
|
|
|
|
|
|
|
} |
|
2822
|
8
|
|
|
|
|
10566
|
$r *= $rscale; |
|
2823
|
8
|
|
|
|
|
1809
|
$out->(0:1) .= $dcyz * $rscale->dummy(0,1); |
|
2824
|
|
|
|
|
|
|
|
|
2825
|
|
|
|
|
|
|
# Chuck points that are outside the FOV: glue those points |
|
2826
|
|
|
|
|
|
|
# onto the removal list. The conditional works around a bug |
|
2827
|
|
|
|
|
|
|
# in 2.3.4cvs and earlier: null piddles make append() crash. |
|
2828
|
8
|
|
|
|
|
10518
|
my $w; |
|
2829
|
8
|
50
|
|
|
|
75
|
if(ref $o->{f} eq 'ARRAY') { |
|
2830
|
|
|
|
|
|
|
$w = whichND( ( abs($dcyz->((0))) > $o->{f}->[0] ) | |
|
2831
|
0
|
|
|
|
|
0
|
( abs($dcyz->((1))) > $o->{f}->[1] ) | |
|
2832
|
|
|
|
|
|
|
($r < 0) |
|
2833
|
|
|
|
|
|
|
); |
|
2834
|
|
|
|
|
|
|
} else { |
|
2835
|
8
|
|
|
|
|
18660
|
$w = whichND( ($r > $o->{f}) | ($r < 0) ); |
|
2836
|
|
|
|
|
|
|
} |
|
2837
|
|
|
|
|
|
|
|
|
2838
|
8
|
0
|
|
|
|
7674
|
$idx = ($idx->nelem) ? $idx->glue(1,$w) : $w |
|
|
|
50
|
|
|
|
|
|
|
2839
|
|
|
|
|
|
|
if($w->nelem); |
|
2840
|
|
|
|
|
|
|
|
|
2841
|
8
|
50
|
|
|
|
37
|
if($idx->nelem) { |
|
2842
|
8
|
|
|
|
|
44
|
$out->((0))->range($idx) .= $o->{bad}; |
|
2843
|
8
|
|
|
|
|
61853
|
$out->((1))->range($idx) .= $o->{bad}; |
|
2844
|
|
|
|
|
|
|
} |
|
2845
|
|
|
|
|
|
|
|
|
2846
|
|
|
|
|
|
|
## Scale by the output conversion factor |
|
2847
|
8
|
|
|
|
|
62083
|
$out->(0:1) /= $o->{tconv}; |
|
2848
|
|
|
|
|
|
|
|
|
2849
|
8
|
|
|
|
|
17153
|
$out; |
|
2850
|
1
|
|
|
|
|
120
|
}; |
|
2851
|
|
|
|
|
|
|
|
|
2852
|
|
|
|
|
|
|
|
|
2853
|
|
|
|
|
|
|
# |
|
2854
|
|
|
|
|
|
|
# Inverse function |
|
2855
|
|
|
|
|
|
|
# |
|
2856
|
|
|
|
|
|
|
$me->{inv} = sub { |
|
2857
|
0
|
|
|
0
|
|
0
|
my($d,$o) = @_; |
|
2858
|
|
|
|
|
|
|
|
|
2859
|
0
|
0
|
|
|
|
0
|
my($out) = $d->is_inplace ? $d : $d->copy; |
|
2860
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $o->{tconv}; |
|
2861
|
|
|
|
|
|
|
|
|
2862
|
0
|
|
|
|
|
0
|
my $oyz = $out->(0:1) ; |
|
2863
|
|
|
|
|
|
|
|
|
2864
|
|
|
|
|
|
|
## Inverse-magnify if required |
|
2865
|
0
|
0
|
|
|
|
0
|
if($o->{mag} != 1.0) { |
|
2866
|
0
|
|
|
|
|
0
|
my $r = ($oyz * $oyz)->sumover->sqrt; |
|
2867
|
0
|
|
|
|
|
0
|
my $scale = tan( atan( $r ) / $o->{mag} ) / $r; |
|
2868
|
0
|
|
|
|
|
0
|
$out->(0:1) *= $scale->dummy(0,1); |
|
2869
|
|
|
|
|
|
|
} |
|
2870
|
|
|
|
|
|
|
|
|
2871
|
|
|
|
|
|
|
## Solve for the X coordinate of the surface. |
|
2872
|
|
|
|
|
|
|
## This is a quadratic in the tangent-plane coordinates; |
|
2873
|
|
|
|
|
|
|
## so here we just figure out the coefficients and plug into |
|
2874
|
|
|
|
|
|
|
## the quadratic formula. $y here is actually -B/2. |
|
2875
|
0
|
|
|
|
|
0
|
my $a1 = ($oyz * $oyz)->sumover + 1; |
|
2876
|
|
|
|
|
|
|
my $y = ( $o->{sph_origin}->((0)) |
|
2877
|
0
|
|
|
|
|
0
|
- ($o->{sph_origin}->(1:2) * $oyz)->sumover |
|
2878
|
|
|
|
|
|
|
); |
|
2879
|
0
|
|
|
|
|
0
|
my $c = topdl($o->{r0}*$o->{r0} - 1); |
|
2880
|
|
|
|
|
|
|
|
|
2881
|
0
|
|
|
|
|
0
|
my $x; |
|
2882
|
0
|
0
|
|
|
|
0
|
if($o->{m} == 2) { |
|
2883
|
|
|
|
|
|
|
# Exceptional case: mask asks for the far hemisphere |
|
2884
|
0
|
|
|
|
|
0
|
$x = - ( $y - sqrt($y*$y - $a1 * $c) ) / $a1; |
|
2885
|
|
|
|
|
|
|
} else { |
|
2886
|
|
|
|
|
|
|
# normal case: mask asks for the near hemisphere |
|
2887
|
0
|
|
|
|
|
0
|
$x = - ( $y + sqrt($y*$y - $a1 * $c) ) / $a1; |
|
2888
|
|
|
|
|
|
|
} |
|
2889
|
|
|
|
|
|
|
|
|
2890
|
|
|
|
|
|
|
## Assemble the 3-space coordinates of the points |
|
2891
|
0
|
|
|
|
|
0
|
my $int = $out->(0)->append($out); |
|
2892
|
0
|
|
|
|
|
0
|
$int->sever; |
|
2893
|
0
|
|
|
|
|
0
|
$int->((0)) .= -1.0; |
|
2894
|
0
|
|
|
|
|
0
|
$int->(0:2) *= $x->dummy(0,3); |
|
2895
|
|
|
|
|
|
|
|
|
2896
|
|
|
|
|
|
|
## convert back to (lon,lat) coordinates... |
|
2897
|
0
|
|
|
|
|
0
|
$out .= $int->invert($o->{prefilt}); |
|
2898
|
|
|
|
|
|
|
|
|
2899
|
|
|
|
|
|
|
# If we're outside the sphere, do hemisphere filtering |
|
2900
|
0
|
|
|
|
|
0
|
my $idx; |
|
2901
|
0
|
0
|
|
|
|
0
|
if(abs($o->{r0}) < 1 ) { |
|
2902
|
0
|
|
|
|
|
0
|
$idx = null; |
|
2903
|
|
|
|
|
|
|
} else { |
|
2904
|
|
|
|
|
|
|
# Great-circle distance to origin |
|
2905
|
|
|
|
|
|
|
my($cos_c) = ( sin($o->{o}->((1))) * sin($out->((1))) |
|
2906
|
|
|
|
|
|
|
+ |
|
2907
|
|
|
|
|
|
|
cos($o->{o}->((1))) * cos($out->((1))) * |
|
2908
|
0
|
|
|
|
|
0
|
cos($out->((0)) - $o->{o}->((0))) |
|
2909
|
|
|
|
|
|
|
); |
|
2910
|
|
|
|
|
|
|
|
|
2911
|
0
|
|
|
|
|
0
|
my($thresh) = (1.0/$o->{r0}); |
|
2912
|
|
|
|
|
|
|
|
|
2913
|
0
|
0
|
|
|
|
0
|
if($o->{m}==1) { |
|
|
|
0
|
|
|
|
|
|
|
2914
|
0
|
|
|
|
|
0
|
$idx = whichND($cos_c < $thresh); |
|
2915
|
|
|
|
|
|
|
} elsif($o->{m}==2) { |
|
2916
|
0
|
|
|
|
|
0
|
$idx = whichND($cos_c > $thresh); |
|
2917
|
|
|
|
|
|
|
} |
|
2918
|
|
|
|
|
|
|
else { |
|
2919
|
0
|
|
|
|
|
0
|
$idx = null; |
|
2920
|
|
|
|
|
|
|
} |
|
2921
|
|
|
|
|
|
|
} |
|
2922
|
|
|
|
|
|
|
|
|
2923
|
|
|
|
|
|
|
## Convert to the units the user requested |
|
2924
|
0
|
|
|
|
|
0
|
$out->(0:1) /= $o->{conv}; |
|
2925
|
|
|
|
|
|
|
|
|
2926
|
|
|
|
|
|
|
## Mark bad values |
|
2927
|
0
|
0
|
|
|
|
0
|
if($idx->nelem) { |
|
2928
|
0
|
|
|
|
|
0
|
$out->((0))->range($idx) .= $o->{bad}; |
|
2929
|
0
|
|
|
|
|
0
|
$out->((1))->range($idx) .= $o->{bad}; |
|
2930
|
|
|
|
|
|
|
} |
|
2931
|
|
|
|
|
|
|
|
|
2932
|
0
|
|
|
|
|
0
|
$out; |
|
2933
|
|
|
|
|
|
|
|
|
2934
|
1
|
|
|
|
|
6
|
}; |
|
2935
|
|
|
|
|
|
|
|
|
2936
|
1
|
|
|
|
|
13
|
$me; |
|
2937
|
|
|
|
|
|
|
} |
|
2938
|
|
|
|
|
|
|
|
|
2939
|
|
|
|
|
|
|
1; |