mapproj.tcl

Go to the documentation of this file.
00001 /*  mapproj.tcl --*/
00002 /* */
00003 /*  Package for map projections.*/
00004 /* */
00005 /*  Copyright (c) 2007 by Kevin B. Kenny.*/
00006 /* */
00007 /*  See the file "license.terms" for information on usage and redistribution*/
00008 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00009 /* */
00010 /*  RCS: @(#) $Id: mapproj.tcl,v 1.1 2007/08/24 22:36:35 kennykb Exp $*/
00011 /* ------------------------------------------------------------------------------*/
00012 
00013 package require Tcl 8.4
00014 package require math::interpolate 1.0
00015 package require math::special 0.2.1
00016 
00017 package provide mapproj 1.0
00018 
00019 /*  ::mapproj --*/
00020 /* */
00021 /*  Namespace holding the procedures and values.*/
00022 
00023 namespace ::mapproj {
00024 
00025     /*  Cylindrical projections*/
00026 
00027     namespace export toPlateCarree fromPlateCarree
00028     namespace export toCassini fromCassini
00029     namespace export toCylindricalEqualArea fromCylindricalEqualArea
00030     namespace export toMercator fromMercator
00031 
00032     /*  Named cylindric equal-area projections with specific*/
00033     /*  standard parallels*/
00034 
00035     namespace export toLambertCylindricalEqualArea \
00036     fromLambertCylindricalEqualArea
00037     namespace export toBehrmann fromBehrmann
00038     namespace export toTrystanEdwards fromTrystanEdwards
00039     namespace export toHoboDyer fromHoboDyer
00040     namespace export toGallPeters fromGallPeters
00041     namespace export toBalthasart fromBalthasart
00042 
00043     /*  Pseudocylindrical projections - equal area*/
00044 
00045     namespace export toSinusoidal fromSinusoidal
00046     namespace export toMillerCylindrical fromMillerCylindrical
00047     namespace export toMollweide fromMollweide
00048     namespace export toEckertIV fromEckertIV toEckertVI fromEckertVI
00049 
00050     /*  Pseudocylindrical projections - compromise*/
00051 
00052     namespace export toRobinson fromRobinson
00053 
00054     /*  Azimuthal projections*/
00055 
00056     namespace export toAzimuthalEquidistant fromAzimuthalEquidistant
00057     namespace export toLambertAzimuthalEqualArea fromLambertAzimuthalEqualArea
00058     namespace export toStereographic fromStereographic
00059     namespace export toOrthographic fromOrthographic
00060     namespace export toGnomonic fromGnomonic
00061 
00062     /*  Pseudo-azimuthal projections*/
00063 
00064     namespace export toHammer fromHammer
00065 
00066     /*  Conic projections*/
00067 
00068     namespace export toConicEquidistant fromConicEquidistant
00069     namespace export toLambertConformalConic fromLambertConformalConic
00070     namespace export toAlbersEqualAreaConic fromAlbersEqualAreaConic
00071 
00072     /*  Miscellaneous projections*/
00073 
00074     namespace export toPeirceQuincuncial fromPeirceQuincuncial
00075 
00076     /*  Fundamental constants*/
00077 
00078     variable pi [expr {acos(-1.0)}]
00079     variable twopi [expr {2.0 * $pi}]
00080     variable halfpi [expr {0.5 * $pi}]
00081     variable quarterpi [expr {0.25 * $pi}]
00082     variable threequarterpi [expr {0.75 * $pi}]
00083     variable mquarterpi [expr {-0.25 * $pi}]
00084     variable mthreequarterpi [expr {-0.75 * $pi}]
00085     variable radian [expr {180. / $pi}]
00086     variable degree [expr {$pi / 180.}]
00087     variable sqrt2 [expr {sqrt(2.)}]
00088     variable sqrt8 [expr {2. * $sqrt2}]
00089     variable halfSqrt3 [expr {sqrt(3.) / 2.}]
00090     variable halfSqrt2 [expr {sqrt(2.) / 2.}]
00091     variable EckertIVK1 [expr {2.0 / sqrt($pi * (4.0 + $pi))}]
00092     variable EckertIVK2 [expr {2.0 * sqrt($pi / (4.0 + $pi))}]
00093     variable EckertVIK1 [expr {sqrt(2.0 + $pi)}]
00094     variable PeirceQuincuncialScale 3.7081493546027438 ;/*  2*K(1/2)*/
00095     variable PeirceQuincuncialLimit 1.8540746773013719 ;/*  K(1/2)*/
00096 
00097     /*  Table of parallel length and distance from equator for the*/
00098     /*  Robinson projection*/
00099 
00100     variable RobinsonLatitude {
00101     -90.0   -85.0   -80.0   -75.0   -70.0   -65.0
00102     -60.0   -55.0   -50.0   -45.0   -40.0   -35.0
00103     -30.0   -25.0   -20.0   -15.0   -10.0   -5.0
00104     0.0 5.0 10.0    15.0    20.0    25.0    30.0
00105     35.0    40.0    45.0    50.0    55.0    60.0
00106     65.0    70.0    75.0    80.0    85.0    90.0
00107     }
00108     variable RobinsonPLEN {
00109     0.5322  0.5722  0.6213  0.6732  0.7186  0.7597
00110     0.7986  0.8350  0.8679  0.8962  0.9216  0.9427
00111     0.9600  0.9730  0.9822  0.9900  0.9954  0.9986
00112     1.0000  0.9986  0.9954  0.9900  0.9822  0.9730  0.9600
00113     0.9427  0.9216  0.8962  0.8679  0.8350  0.7986
00114     0.7597  0.7186  0.6732  0.6213  0.5722  0.5322
00115     }
00116     variable RobinsonPDFE {
00117     -1.0000 -0.9761 -0.9394 -0.8936 -0.8435 -0.7903
00118     -0.7346 -0.6769 -0.6176 -0.5571 -0.4958 -0.4340
00119     -0.3720 -0.3100 -0.2480 -0.1860 -0.1240 -0.0620
00120     0.0000  0.0620  0.1240  0.1860  0.2480  0.3100  0.3720
00121     0.4340  0.4958  0.5571  0.6176  0.6769  0.7346
00122     0.7903  0.8435  0.8936  0.9394  0.9761  1.0000
00123     }
00124 
00125     /*  Interpolation tables for Robinson*/
00126 
00127     variable RobinsonSplinePLEN \
00128     [math::interpolate::prepare-cubic-splines \
00129          $RobinsonLatitude $RobinsonPLEN]
00130     variable RobinsonSplinePDFE \
00131     [math::interpolate::prepare-cubic-splines \
00132          $RobinsonLatitude $RobinsonPDFE]
00133     variable RobinsonM [expr {0.5072 * $pi}]
00134 
00135     namespace import ::math::special::cn
00136 
00137 }
00138 
00139 /*  ::mapproj::ellF - */
00140 /* */
00141 /*  Computes the Legendre incomplete elliptic integral of the*/
00142 /*  first kind:*/
00143 /* */
00144 /*  F(phi, k) = \integral_0^phi dtheta/sqrt(1 - k**2 sin**2 theta)*/
00145 /* */
00146 /* */
00147 /*  Parameters:*/
00148 /*  phi -- Limit of integration; angle around the ellipse*/
00149 /*  k -- Eccentricity*/
00150 /* */
00151 /*  Results:*/
00152 /*  Returns F(phi, k)*/
00153 /* */
00154 /*  Notes:*/
00155 /*  We compute this integral in terms of the Carlson elliptic integral*/
00156 /*  ellRF(x, y, z).*/
00157 
00158 ret  ::mapproj::ellF (type phi , type k) {
00159     return [ellFaux [expr {cos($phi)}] [expr {sin($phi)}] $k]
00160 }
00161 
00162 /*  ::mapproj::ellFaux -*/
00163 /* */
00164 /*  Computes the Legendre incomplete elliptic integral of the*/
00165 /*  first kind when circular functions of the 'phi' argument*/
00166 /*  are already available.*/
00167 /* */
00168 /*  Parameters:*/
00169 /*  cos_phi - Cosine of the argument*/
00170 /*  sin_phi - Sine of the argument*/
00171 /*  k - Parameter*/
00172 /* */
00173 /*  Results:*/
00174 /*  Returns F(atan(sin_phi/cos_phi), k)*/
00175 
00176 ret  ::mapproj::ellFaux (type cos_, type phi , type sin_, type phi , type k) {
00177     set rf [ellRF [expr {$cos_phi * $cos_phi}] \
00178            [expr {1.0 - $k * $k * $sin_phi * $sin_phi}] \
00179            1.0]
00180     return [expr {$sin_phi * $rf}]
00181 }
00182 
00183 /*  ::mapproj::ellRF --*/
00184 /* */
00185 /*  Computes the Carlson incomplete elliptic integral of the*/
00186 /*  first kind:*/
00187 /* */
00188 /*  RF(x, y, z) = 1/2 * integral_0^inf dt/sqrt((t+x)*(t+y)*(t+z))*/
00189 /* */
00190 /*  Parameters:*/
00191 /*  x, y, z -- Interchangeable parameters of the integral*/
00192 /* */
00193 /*  Results:*/
00194 /*  Returns the value of RF*/
00195 
00196 ret  ::mapproj::ellRF (type x , type y , type z) {
00197     if {$x < 0.0 || $y < 0.0 || $z < 0.0} {
00198     return -code error "Negative argument to Carlson's ellRF" \
00199         -errorCode "ellRF negArgument"
00200     }
00201     set delx 1.0; set dely 1.0; set delz 1.0
00202     while {abs($delx) > 0.0025 || abs($dely) > 0.0025 || abs($delz) > 0.0025} {
00203     set sx [expr {sqrt($x)}]
00204     set sy [expr {sqrt($y)}]
00205     set sz [expr {sqrt($z)}]
00206     set len [expr {$sx * ($sy + $sz) + $sy * $sz}]
00207     set x [expr {0.25 * ($x + $len)}]
00208     set y [expr {0.25 * ($y + $len)}]
00209     set z [expr {0.25 * ($z + $len)}]
00210     set mean [expr {($x + $y + $z) / 3.0}]
00211     set delx [expr {($mean - $x) / $mean}]
00212     set dely [expr {($mean - $y) / $mean}]
00213     set delz [expr {($mean - $z) / $mean}]
00214     }
00215     set e2 [expr {$delx * $dely - $delz * $delz}]
00216     set e3 [expr {$delx * $dely * $delz}]
00217     return [expr {(1.0 + ($e2 / 24.0 - 0.1 - 3.0 * $e3 / 44.0) * $e2
00218            + $e3 / 14.) / sqrt($mean)}]
00219 }
00220 
00221 /*  ::mapproj::toPlateCarree --*/
00222 /* */
00223 /*  Project a latitude and longitude onto the plate carrée.*/
00224 /* */
00225 /*  Parameters:*/
00226 /*  phi_0 -- Latitude of the center of the sheet in degrees*/
00227 /*  lambda_0 -- Longitude of the center of sheet in degrees*/
00228 /*  lambda -- Longitude of the point to be projected in degrees*/
00229 /*  phi -- Latitude of the point to be projected in degrees*/
00230 /* */
00231 /*  Results:*/
00232 /*  Returns x and y co-ordinates.  Units of x and y are Earth radii;*/
00233 /*  scale is true at the Equator.*/
00234 
00235 ret  ::mapproj::toPlateCarree (type lambda_0 , type phi_0 , type lambda , type phi) {
00236     variable degree
00237     set lambda [expr {$lambda - $lambda_0 + 180.}]
00238     if {$lambda < 0.0 || $lambda > 360.0} {
00239     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00240     }
00241     set lambda [expr {$lambda - 180.}]
00242     set x [expr {$lambda * $degree}]
00243     set y [expr {($phi - $phi_0) * $degree}]
00244     return [list $x $y]
00245 }
00246 
00247 /*  ::mapproj::fromPlateCarree --*/
00248 /* */
00249 /*  Solve a plate carrée projection for the*/
00250 /*  latitude and longitude represented by a point on the map.*/
00251 /* */
00252 /*  Parameters:*/
00253 /*  phi_0 -- Latitude of the center of the sheet in degrees*/
00254 /*  lambda_0 -- Longitude of the center of projection*/
00255 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00256 /* */
00257 /*  Results:*/
00258 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00259 
00260 ret  ::mapproj::fromPlateCarree (type phi_0 , type lambda_0 , type x , type y) {
00261     variable radian
00262     set lambda [expr {$lambda_0 + $x * $radian + 180.}]
00263     if {$lambda < 0.0 || $lambda > 360.0} {
00264     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00265     }
00266     set lambda [expr {$lambda - 180.}]
00267     set phi [expr {$y * $radian + $phi_0}]
00268     return [list $lambda $phi]
00269 }
00270 
00271 /*  mapproj::toCylindricalEqualArea --*/
00272 /* */
00273 /*  Project a latitude and longitude into cylindrical equal-area*/
00274 /*  co-ordinates.*/
00275 /* */
00276 /*  Parameters:*/
00277 /*  phi_1 --    Standard latitude in degrees*/
00278 /*  phi_0 -- Latitude of the center of the sheet in degrees*/
00279 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00280 /*  lambda --   Longitude of the point to be projected in degrees*/
00281 /*  phi -- Latitude of the point to be projected in degrees*/
00282 /* */
00283 /*  Results:*/
00284 /*  Returns x and y co-ordinates.  Units of x and y are Earth radii;*/
00285 /*  scale is true at the reference latitude*/
00286 
00287 ret  ::mapproj::toCylindricalEqualArea (type phi_1 , type lambda_0 , type phi_0 , type lambda , type phi) {
00288     variable degree
00289     set cos_phi_s [expr {cos($phi_1 * $degree)}]
00290     set lambda [expr {$lambda - $lambda_0 + 180.}]
00291     if {$lambda < 0.0 || $lambda > 360.0} {
00292     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00293     }
00294     set lambda [expr {$lambda - 180.}]
00295     set x [expr {$lambda * $degree * $cos_phi_s}]
00296     set y0 [expr {sin($phi_0 * $degree) / $cos_phi_s}]
00297     set y [expr {sin($phi * $degree) / $cos_phi_s}]
00298     return [list $x [expr {$y - $y0}]]
00299 }
00300 
00301 /*  ::mapproj::fromCylindricalEqualArea --*/
00302 /* */
00303 /*  Solve a cylindrical equal area map projection for the*/
00304 /*  latitude and longitude represented by a point on the map.*/
00305 /* */
00306 /*  Parameters:*/
00307 /*  phi_1 -- Standard latitude in degrees*/
00308 /*  phi_0 -- Latitude of the center of the sheet in degrees*/
00309 /*  lambda_0 -- Longitude of the center of sheet in degrees*/
00310 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00311 /* */
00312 /*  Results:*/
00313 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00314 
00315 ret  ::mapproj::fromCylindricalEqualArea (type phi_1 , type lambda_0 , type phi_0 , type x , type y) {
00316     variable degree
00317     variable radian
00318     set cos_phi_s [expr {cos($phi_1 * $degree)}]
00319     set lambda [expr {$lambda_0 + $x / $cos_phi_s * $radian + 180.}]
00320     if {$lambda < 0.0 || $lambda > 360.0} {
00321     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00322     }
00323     set lambda [expr {$lambda - 180.}]
00324     set y0 [expr {sin($phi_0 * $degree) / $cos_phi_s}]
00325     set phi [expr {asin(($y + $y0) * $cos_phi_s) * $radian}]
00326     return [list $lambda $phi]
00327 }
00328 
00329 /*  ::mapproj::toMercator --*/
00330 /* */
00331 /*  Project a latitude and longitude into the Mercator projection*/
00332 /*  co-ordinates.*/
00333 /* */
00334 /*  Parameters:*/
00335 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00336 /*  phi_0 -- Latitude of the center of sheet in degrees*/
00337 /*  lambda --   Longitude of the point to be projected in degrees*/
00338 /*  phi -- Latitude of the point to be projected in degrees*/
00339 /* */
00340 /*  Results:*/
00341 /*  Returns x and y co-ordinates in Earth radii. Scale is true*/
00342 /*  at the Equator and increases without bounds toward the Poles.*/
00343 
00344 ret  ::mapproj::toMercator (type lambda_0 , type phi_0 , type lambda , type phi) {
00345     variable trace; if {[info exists trace]} { puts [info level 0] }
00346     variable degree
00347     variable quarterpi
00348     set lambda [expr {$lambda - $lambda_0 + 180.}]
00349     if {$lambda < 0.0 || $lambda > 360.0} {
00350     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00351     }
00352     set lambda [expr {$lambda - 180.}]
00353     set x [expr {$lambda * $degree}]
00354     set y [expr {log(tan($quarterpi + 0.5 * $phi * $degree))}]
00355     set y0 [expr {log(tan($quarterpi + 0.5 * $phi_0 * $degree))}]
00356     if {[info exists trace]} { puts "[info level 0] -> $x [expr {$y - $y0}]" }
00357     return [list $x [expr {$y - $y0}]]
00358 }
00359 
00360 /*  ::mapproj::fromMercator --*/
00361 /* */
00362 /*  Converts Mercator map co-ordinates to latitude and longitude.*/
00363 /* */
00364 /*  Parameters:*/
00365 /*  lambda_0 -- Longitude of the center of projection*/
00366 /*  phi_0 -- Latitude of the center of sheet in degrees*/
00367 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00368 /* */
00369 /*  Results:*/
00370 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00371 
00372 ret  ::mapproj::fromMercator (type lambda_0 , type phi_0 , type x , type y) {
00373     variable quarterpi
00374     variable degree
00375     variable radian
00376     set y0 [expr {log(tan($quarterpi + 0.5 * $phi_0 * $degree))}]
00377     set lambda [expr {$lambda_0 + $x  * $radian + 180.}]
00378     if {$lambda < 0.0 || $lambda > 360.0} {
00379     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00380     }
00381     set lambda [expr {$lambda - 180.}]
00382     set phi [expr {$radian * 2.0 * atan(exp($y + $y0)) - 90.0}]
00383     return [list $lambda $phi]
00384 }
00385 
00386 /*  ::mapproj::toMillerCylindrical --*/
00387 /* */
00388 /*  Project a latitude and longitude into the Miller Cylindrical projection*/
00389 /*  co-ordinates.*/
00390 /* */
00391 /*  Parameters:*/
00392 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00393 /*  lambda --   Longitude of the point to be projected in degrees*/
00394 /*  phi -- Latitude of the point to be projected in degrees*/
00395 /* */
00396 /*  Results:*/
00397 /*  Returns x and y co-ordinates.  The x co-ordinate ranges from*/
00398 /*  -$pi to pi.*/
00399 
00400 ret  ::mapproj::toMillerCylindrical (type lambda_0 , type lambda , type phi) {
00401     foreach {x y} [toMercator $lambda_0 0.0 \
00402                $lambda [expr {0.8 * $phi}]] break
00403     set y [expr {1.25 * $y}]
00404     return [list $x $y]
00405 }
00406 
00407 /*  ::mapproj::fromMillerCylindrical --*/
00408 /* */
00409 /*  Converts Miller Cylindrical projected  map co-ordinates */
00410 /*  to latitude and longitude.*/
00411 /* */
00412 /*  Parameters:*/
00413 /*  lambda_0 -- Longitude of the center of projection*/
00414 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00415 /* */
00416 /*  Results:*/
00417 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00418 
00419 ret  ::mapproj::fromMillerCylindrical (type lambda_0 , type x , type y) {
00420     foreach {lambda phi} [fromMercator $lambda_0 0.0 \
00421                   $x [expr {0.8 * $y}]] break
00422     return [list $lambda [expr {1.25 * $phi}]]
00423 }
00424 
00425 /*  ::mapproj::toSinusoidal --*/
00426 /* */
00427 /*  Project a latitude and longitude into the sinusoidal*/
00428 /*  (Sanson-Flamsteed) projection.*/
00429 /* */
00430 /*  Parameters:*/
00431 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00432 /*  phi_0 -- Latitude of the center of the sheet, in degrees*/
00433 /*  lambda --   Longitude of the point to be projected in degrees*/
00434 /*  phi -- Latitude of the point to be projected in degrees*/
00435 /* */
00436 /*  Results:*/
00437 /*  Returns x and y co-ordinates, in Earth radii. */
00438 /*  Scale is true along the Equator and central meridian.*/
00439 
00440 ret  ::mapproj::toSinusoidal (type lambda_0 , type phi_0 , type lambda , type phi) {
00441     variable degree
00442     variable quarterpi
00443     set lambda [expr {$lambda - $lambda_0 + 180.}]
00444     if {$lambda < 0.0 || $lambda > 360.0} {
00445     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00446     }
00447     set lambda [expr {($lambda - 180.) * $degree}]
00448     set phi [expr {$phi * $degree}]
00449     set x [expr {$lambda * cos($phi)}]
00450     set phi [expr {$phi - $phi_0 * $degree}]
00451     return [list $x $phi]
00452 }
00453 
00454 /*  ::mapproj::fromSinusoidal --*/
00455 /* */
00456 /*  Converts sinusoidal (Sanson-Flamsteed) map co-ordinates */
00457 /*  to latitude and longitude.*/
00458 /* */
00459 /*  Parameters:*/
00460 /*  lambda_0 -- Longitude of the center of projection*/
00461 /*  phi_0 -- Latitude of the center of the sheet, in degrees*/
00462 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00463 /* */
00464 /*  Results:*/
00465 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00466 
00467 ret  ::mapproj::fromSinusoidal (type lambda_0 , type phi_0 , type x , type y) {
00468     variable degree
00469     variable radian
00470     set y [expr {$y + $phi_0 * $degree}]
00471     set phi [expr {$y * $radian}]
00472     set lambda [expr {180. + $lambda_0 + $radian * $x / cos($y)}]
00473     if {$lambda < 0.0 || $lambda > 360.0} {
00474     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00475     }
00476     set lambda [expr {$lambda - 180.}]
00477     return [list $lambda $phi]
00478 }
00479 
00480 /*  ::mapproj::toMollweide --*/
00481 /* */
00482 /*  Project a latitude and longitude into the Mollweide projection*/
00483 /*  co-ordinates.*/
00484 /* */
00485 /*  Parameters:*/
00486 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00487 /*  lambda --   Longitude of the point to be projected in degrees*/
00488 /*  phi -- Latitude of the point to be projected in degrees*/
00489 /* */
00490 /*  Results:*/
00491 /*  Returns x and y co-ordinates, in Earth radii. */
00492 /*  Scale is true along the 40 deg 44 min parallels*/
00493 
00494 ret  ::mapproj::toMollweide (type lambda_0 , type lambda , type phi) {
00495     variable degree
00496     variable pi
00497     variable halfpi
00498     variable sqrt2
00499     variable sqrt8
00500     set lambda [expr {$lambda - $lambda_0 + 180.}]
00501     if {$lambda < 0.0 || $lambda > 360.0} {
00502     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00503     }
00504     set lambda [expr {($lambda - 180.) * $degree}]
00505     set phi [expr {$phi * $degree}]
00506     set theta [expr {2.0 * asin(2.0 * $phi / $pi)}]
00507     set diff 1.0
00508     set pisinphi [expr {$pi * sin($phi)}]
00509     while {abs($diff) >= 1.0e-4} {
00510     set diff [expr {($theta + sin($theta) - $pisinphi)
00511             / (1.0 + cos($theta))}]
00512     set theta [expr {$theta - $diff}]
00513     }
00514     set theta [expr {0.5 * $theta}]
00515     set x [expr {$sqrt8 * $lambda * cos($theta) / $pi}]
00516     set y [expr {$sqrt2 * sin($theta)}]
00517     return [list $x $y]
00518 }
00519 
00520 /*  ::mapproj::fromMollweide --*/
00521 /* */
00522 /*  Converts Mollweide projected  map co-ordinates to latitude */
00523 /*  and longitude.*/
00524 /* */
00525 /*  Parameters:*/
00526 /*  lambda_0 -- Longitude of the center of projection*/
00527 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00528 /* */
00529 /*  Results:*/
00530 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00531 
00532 ret  ::mapproj::fromMollweide (type lambda_0 , type x , type y) {
00533     variable pi
00534     variable radian
00535     variable degree
00536     variable halfpi
00537     variable sqrt2
00538     variable sqrt8
00539     set theta [expr {asin($y / $sqrt2)}]
00540     set lambda [expr {$lambda_0 + $radian * $pi * $x / 
00541               ($sqrt8 * cos($theta)) + 180.}]
00542     set phi [expr {asin((2.0 * $theta + sin(2.0 * $theta)) / $pi)}]
00543     if {$lambda < 0.0 || $lambda > 360.0} {
00544     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00545     }
00546     set lambda [expr {$lambda - 180.}]
00547     return [list $lambda [expr {$phi * $radian}]]
00548 }
00549 
00550 /*  ::mapproj::toEckertIV --*/
00551 /* */
00552 /*  Project a latitude and longitude into the Eckert IV projection*/
00553 /*  co-ordinates.*/
00554 /* */
00555 /*  Parameters:*/
00556 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00557 /*  lambda --   Longitude of the point to be projected in degrees*/
00558 /*  phi -- Latitude of the point to be projected in degrees*/
00559 /* */
00560 /*  Results:*/
00561 /*  Returns x and y co-ordinates.  Scale is true along the 40 deg 30 min*/
00562 /*  parallels.*/
00563 
00564 ret  ::mapproj::toEckertIV (type lambda_0 , type lambda , type phi) {
00565     variable degree
00566     variable pi
00567     variable halfpi
00568     variable EckertIVK1
00569     variable EckertIVK2
00570     set lambda [expr {$lambda - $lambda_0 + 180.}]
00571     if {$lambda < 0.0 || $lambda > 360.0} {
00572     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00573     }
00574     set lambda [expr {($lambda - 180.) * $degree}]
00575     set phi [expr {$phi * $degree}]
00576     set theta [expr {$phi / 2}]
00577     set diff 1.0
00578     set A [expr {(2.0 + $halfpi) * sin($phi)}]
00579     while {abs($diff) >= 1.0e-4} {
00580     set costheta [expr {cos($theta)}]
00581     set sintheta [expr {sin($theta)}]
00582     set diff \
00583         [expr {($theta + $sintheta * $costheta + 2.0 * sin($theta) - $A)
00584            / (2.0 * $costheta * (1.0 + $costheta))}]
00585     set theta [expr {$theta - $diff}]
00586     }
00587     set x [expr {$EckertIVK1 * $lambda * (1.0 + cos($theta))}]
00588     set y [expr {$EckertIVK2 * sin($theta)}]
00589     return [list $x $y]
00590 }
00591 
00592 /*  ::mapproj::fromEckertIV --*/
00593 /* */
00594 /*  Converts Eckert IV projected  map co-ordinates to latitude */
00595 /*  and longitude.*/
00596 /* */
00597 /*  Parameters:*/
00598 /*  lambda_0 -- Longitude of the center of projection*/
00599 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00600 /* */
00601 /*  Results:*/
00602 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00603 
00604 ret  ::mapproj::fromEckertIV (type lambda_0 , type x , type y) {
00605     variable pi
00606     variable radian
00607     variable degree
00608     variable halfpi
00609     variable sqrt2
00610     variable EckertIVK1
00611     variable EckertIVK2
00612     set sintheta [expr {$y / $EckertIVK2}]
00613     set costheta [expr {sqrt(1.0 - $sintheta * $sintheta)}]
00614     set theta [expr {atan2($sintheta, $costheta)}]
00615     set phi [expr {asin(($theta + $sintheta*$costheta + 2.*$sintheta)
00616             / (2. + $halfpi))}]
00617     set lambda [expr {180.0 + $lambda_0
00618               + $radian / $EckertIVK1 * $x / (1.0 + $costheta)}]
00619     if {$lambda < 0.0 || $lambda > 360.0} {
00620     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00621     }
00622     set lambda [expr {$lambda - 180.}]
00623     return [list $lambda [expr {$phi * $radian}]]
00624 }
00625 
00626 /*  ::mapproj::toEckertVI --*/
00627 /* */
00628 /*  Project a latitude and longitude into the Eckert IV projection*/
00629 /*  co-ordinates.*/
00630 /* */
00631 /*  Parameters:*/
00632 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00633 /*  lambda --   Longitude of the point to be projected in degrees*/
00634 /*  phi -- Latitude of the point to be projected in degrees*/
00635 /* */
00636 /*  Results:*/
00637 /*  Returns x and y co-ordinates.  Scale is true along the 40 deg 30 min*/
00638 /*  parallels.*/
00639 
00640 ret  ::mapproj::toEckertVI (type lambda_0 , type lambda , type phi) {
00641     variable degree
00642     variable pi
00643     variable halfpi
00644     variable EckertVIK1
00645     set lambda [expr {$lambda - $lambda_0 + 180.}]
00646     if {$lambda < 0.0 || $lambda > 360.0} {
00647     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00648     }
00649     set lambda [expr {($lambda - 180.) * $degree}]
00650     set phi [expr {$phi * $degree}]
00651     set theta [expr {$phi / 2}]
00652     set diff 1.0
00653     set A [expr {(1.0 + $halfpi) * sin($phi)}]
00654     while {abs($diff) >= 1.0e-4} {
00655     set costheta [expr {cos($theta)}]
00656     set sintheta [expr {sin($theta)}]
00657     set diff \
00658         [expr {($theta + $sintheta - $A)
00659            / (1.0 + $costheta)}]
00660     set theta [expr {$theta - $diff}]
00661     }
00662     set x [expr {$lambda * (1.0 + cos($theta)) / $EckertVIK1}]
00663     set y [expr {2.0 * $theta / $EckertVIK1}]
00664     return [list $x $y]
00665 }
00666 
00667 /*  ::mapproj::fromEckertVI --*/
00668 /* */
00669 /*  Converts Eckert IV projected  map co-ordinates to latitude */
00670 /*  and longitude.*/
00671 /* */
00672 /*  Parameters:*/
00673 /*  lambda_0 -- Longitude of the center of projection*/
00674 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00675 /* */
00676 /*  Results:*/
00677 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00678 
00679 ret  ::mapproj::fromEckertVI (type lambda_0 , type x , type y) {
00680     variable pi
00681     variable radian
00682     variable degree
00683     variable halfpi
00684     variable sqrt2
00685     variable EckertVIK1
00686     puts [info level 0]
00687     set theta [expr {0.5 * $EckertVIK1 * $y}]
00688     puts [list theta = $theta]
00689     set phi [expr {asin(($theta + sin($theta)) / (1.0 + $halfpi))}]
00690     puts [list phi = $phi]
00691     set lambda [expr {180.0 + $lambda_0 + $radian * $EckertVIK1 * $x
00692               / (1 + cos($theta))}]
00693     if {$lambda < 0.0 || $lambda > 360.0} {
00694     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00695     }
00696     set lambda [expr {$lambda - 180.}]
00697     return [list $lambda [expr {$phi * $radian}]]
00698 }
00699 
00700 /*  ::mapproj::toRobinson --*/
00701 /* */
00702 /*  Project a latitude and longitude into the Robinson projection.*/
00703 /* */
00704 /*  Parameters:*/
00705 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00706 /*  lambda --   Longitude of the point to be projected in degrees*/
00707 /*  phi -- Latitude of the point to be projected in degrees*/
00708 /* */
00709 /*  Results:*/
00710 /*  Returns x and y co-ordinates, in Earth radii. */
00711 /*  Scale is true along the Equator.*/
00712 
00713 ret  ::mapproj::toRobinson (type lambda_0 , type lambda , type phi) {
00714     variable RobinsonLatitude
00715     variable RobinsonSplinePLEN
00716     variable RobinsonSplinePDFE
00717     variable RobinsonM
00718     variable pi
00719     variable degree
00720     set lambda [expr {$lambda - $lambda_0 + 180.}]
00721     if {$lambda < 0.0 || $lambda > 360.0} {
00722     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00723     }
00724     set lambda [expr {$lambda - 180.}]
00725     set y [math::interpolate::interp-cubic-splines $RobinsonSplinePDFE $phi]
00726     set y [expr {$RobinsonM * $y}]
00727     set s [math::interpolate::interp-cubic-splines $RobinsonSplinePLEN $phi]
00728     set x [expr {$degree * $s * $lambda}]
00729     return [list $x $y]
00730 }
00731 
00732 /*  ::mapproj::fromRobinson --*/
00733 /* */
00734 /*  Solve the Robinson projection for the*/
00735 /*  latitude and longitude represented by a point on the map.*/
00736 /* */
00737 /*  Parameters:*/
00738 /*  lambda_0 -- Longitude of the center of projection*/
00739 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00740 /* */
00741 /*  Results:*/
00742 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00743 
00744 ret  ::mapproj::fromRobinson (type lambda_0 , type x , type y) {
00745     variable RobinsonLatitude
00746     variable RobinsonPDFE
00747     variable RobinsonSplinePLEN
00748     variable RobinsonSplinePDFE
00749     variable RobinsonM
00750     variable radian
00751 
00752     # We know that Robinson latitudes are equally spaced from [-90..90]
00753     # at 5-degree intervals.  Find the values for RobinsonPDFE that
00754     # bracket the y co-ordinate.
00755 
00756     set y [expr {$y / $RobinsonM}]
00757     set l 0
00758     set u [expr {[llength $RobinsonPDFE] - 1}]
00759     while {$l < $u} {
00760     set m [expr {($l + $u + 1) / 2}]
00761     if {$y >= [lindex $RobinsonPDFE $m]} {
00762         set l $m
00763     } else {
00764         set u [expr {$m - 1}]
00765     }
00766     }
00767     set u [lindex $RobinsonLatitude [expr {$l+1}]]
00768     set l [lindex $RobinsonLatitude $l]
00769     for {set i 0} {$i < 12} {incr i} {
00770     set m [expr {0.5 * ($u + $l)}]
00771     set ystar [math::interpolate::interp-cubic-splines \
00772                $RobinsonSplinePDFE $m]
00773     if {$ystar < $y} {
00774         set l $m
00775     } else {
00776         set u $m
00777     }
00778     }
00779     puts "latitude $m"
00780     set s [math::interpolate::interp-cubic-splines $RobinsonSplinePLEN $m]
00781     puts "parallel length $s"
00782     set lambda [expr {180.0 + $lambda_0 + $radian * $x / $s}]
00783     if {$lambda < 0.0 || $lambda > 360.0} {
00784     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00785     }
00786     set lambda [expr {$lambda - 180.}]
00787     return [list $lambda $m]
00788 }
00789 
00790 /*  ::mapproj::toCassini --*/
00791 /* */
00792 /*  Project a latitude and longitude into the Cassini projection.*/
00793 /* */
00794 /*  Parameters:*/
00795 /*  lambda_0 -- Longitude of the center of projection in degrees*/
00796 /*  phi_0 -- Latitude of the center of the sheet*/
00797 /*  lambda --   Longitude of the point to be projected in degrees*/
00798 /*  phi -- Latitude of the point to be projected in degrees*/
00799 /* */
00800 /*  Results:*/
00801 /*  Returns x and y co-ordinates, in Earth radii. */
00802 /*  Scale is true along the central meridian.*/
00803 
00804 ret  ::mapproj::toCassini (type lambda_0 , type phi_0 , type lambda , type phi) {
00805     variable degree
00806     variable pi
00807     variable twopi
00808     variable quarterpi
00809     set lambda [expr {$lambda - $lambda_0 + 180.}]
00810     if {$lambda < 0.0 || $lambda > 360.0} {
00811     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00812     }
00813     set lambda [expr {($lambda - 180.) * $degree}]
00814     set phi [expr {$phi * $degree}]
00815     set x [expr {asin(cos($phi) * sin($lambda))}]
00816     set y [expr {atan2(tan($phi), cos($lambda)) - $degree * $phi_0}]
00817     if {$y < -$pi} {
00818     set y [expr {$y + $twopi}]
00819     } elseif {$y > $pi} {
00820     set y [expr {$y - $twopi}]
00821     }
00822     return [list $x $y]
00823 }
00824 
00825 /*  ::mapproj::fromCassini --*/
00826 /* */
00827 /*  Converts Cassini map co-ordinates to latitude and longitude.*/
00828 /* */
00829 /*  Parameters:*/
00830 /*  lambda_0 -- Longitude of the center of projection*/
00831 /*  phi_0 -- Latitude of the center of the sheet*/
00832 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00833 /* */
00834 /*  Results:*/
00835 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00836 
00837 ret  ::mapproj::fromCassini (type lambda_0 , type phi_0 , type x , type y) {
00838     variable degree
00839     variable radian
00840     set y [expr {$y + $degree * $phi_0}]
00841     set phi [expr {$radian * asin(cos($x) * sin($y))}]
00842     set lambda [expr {180. + $lambda_0 + $radian * atan2(tan($x), cos($y))}]
00843     if {$lambda < 0.0 || $lambda > 360.0} {
00844     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00845     }
00846     set lambda [expr {$lambda - 180.}]
00847     return [list $lambda $phi]
00848 }
00849 
00850 /*  ::mapproj::toPeirceQuincuncial*/
00851 /* */
00852 /*  Converts geodetic co-ordinates to the Peirce Quincuncial projection.*/
00853 /* */
00854 /*  Parameters:*/
00855 /*  lambda_0 - Longitude of the central meridian.  (Conventionally, 20.0).*/
00856 /*  lambda - Longitude of the point to be projected in degrees*/
00857 /*  phi - Latitude of the point to be projected in degrees.*/
00858 /* */
00859 /*  Results:*/
00860 /*  Returns a list of the x and y co-ordinates.*/
00861 
00862 ret  ::mapproj::toPeirceQuincuncial (type lambda_0 , type lambda , type phi) {
00863     variable degree
00864     variable halfSqrt2
00865     variable pi
00866     variable quarterpi
00867     variable mquarterpi
00868     variable threequarterpi
00869     variable mthreequarterpi
00870     variable PeirceQuincuncialScale
00871 
00872     # Convert latitude and longitude to radians relative to the
00873     # central meridian
00874 
00875     set lambda [expr {$lambda - $lambda_0 + 180.}]
00876     if {$lambda < 0.0 || $lambda > 360.0} {
00877     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
00878     }
00879     set lambda [expr {($lambda - 180.) * $degree}]
00880     set phi [expr {$phi * $degree}]
00881 
00882     # Compute the auxiliary quantities 'm' and 'n'. Set 'm' to match
00883     # the sign of 'lambda' and 'n' to be positive if |lambda| > pi/2
00884 
00885     set cos_phiosqrt2 [expr {$halfSqrt2 * cos($phi)}]
00886     set cos_lambda [expr {cos($lambda)}]
00887     set sin_lambda [expr {sin($lambda)}]
00888     set cos_a [expr {$cos_phiosqrt2 * ($sin_lambda + $cos_lambda)}]
00889     set cos_b [expr {$cos_phiosqrt2 * ($sin_lambda - $cos_lambda)}]
00890     set sin_a [expr {sqrt(1.0 - $cos_a * $cos_a)}]
00891     set sin_b [expr {sqrt(1.0 - $cos_b * $cos_b)}]
00892     set cos_a_cos_b [expr {$cos_a * $cos_b}]
00893     set sin_a_sin_b [expr {$sin_a * $sin_b}]
00894     set sin2_m [expr {1.0 + $cos_a_cos_b - $sin_a_sin_b}]
00895     set sin2_n [expr {1.0 - $cos_a_cos_b - $sin_a_sin_b}]
00896     if {$sin2_m < 0.0} {set sin2_m 0.0}
00897     set sin_m [expr {sqrt($sin2_m)}]
00898     if {$sin2_m > 1.0} { set sin2_m 1.0 }
00899     set cos_m [expr {sqrt(1.0 - $sin2_m)}]
00900     if {$sin_lambda < 0.0} {
00901     set sin_m [expr {-$sin_m}]
00902     }
00903    if {$sin2_n < 0.0} { set sin2_n 0.0 }
00904     set sin_n [expr {sqrt($sin2_n)}]
00905     if {$sin2_n > 1.0} { set sin2_n 1.0 }
00906     set cos_n [expr {sqrt(1.0 - $sin2_n)}]
00907     if {$cos_lambda > 0.0} {
00908     set sin_n [expr {-$sin_n}]
00909     }
00910 
00911     # Compute elliptic integrals to map the disc to the square
00912 
00913     set x [ellFaux $cos_m $sin_m $halfSqrt2]
00914     set y [ellFaux $cos_n $sin_n $halfSqrt2]
00915 
00916     # Reflect the Southern Hemisphere outward
00917 
00918     if {$phi < 0} {
00919     if {$lambda < $mthreequarterpi} {
00920         set y [expr {$PeirceQuincuncialScale - $y}]
00921     } elseif {$lambda < $mquarterpi} {
00922         set x [expr {-$PeirceQuincuncialScale - $x}]
00923     } elseif {$lambda < $quarterpi} {
00924         set y [expr {-$PeirceQuincuncialScale - $y}]
00925     } elseif {$lambda < $threequarterpi} {
00926         set x [expr {$PeirceQuincuncialScale - $x}]
00927     } else {
00928         set y [expr {$PeirceQuincuncialScale - $y}]
00929     }
00930     }
00931 
00932     # Rotate the square by 45 degrees to fit the screen better
00933 
00934     set X [expr {($x - $y) * $halfSqrt2}]
00935     set Y [expr {($x + $y) * $halfSqrt2}]
00936 
00937     return [list $X $Y]
00938 }
00939 
00940 /*  ::mapproj::fromPeirceQuincuncial --*/
00941 /* */
00942 /*  Converts Peirce Quincuncial map co-ordinates to latitude and longitude.*/
00943 /* */
00944 /*  Parameters:*/
00945 /*  lambda_0 -- Longitude of the center of projection*/
00946 /*  x,y -- normalized x and y co-ordinates of a point on the map*/
00947 /* */
00948 /*  Results:*/
00949 /*  Returns a list consisting of the longitude and latitude in degrees.*/
00950 
00951 ret  ::mapproj::fromPeirceQuincuncial (type lambda_0 , type x , type y) {
00952     variable halfSqrt2
00953     variable radian
00954     variable pi
00955     variable halfpi
00956     variable quarterpi
00957     variable PeirceQuincuncialScale
00958     variable PeirceQuincuncialLimit
00959 
00960     # Rotate x and y 45 degrees
00961 
00962     set X [expr {($x + $y) * $halfSqrt2}]
00963     set Y [expr {($y - $x) * $halfSqrt2}]
00964 
00965     # Reflect Southern Hemisphere into the Northern
00966 
00967     set southern 0
00968     if {$X < -$PeirceQuincuncialLimit} {
00969     set X [expr {-$PeirceQuincuncialScale - $X}]
00970     set southern 1
00971     } elseif {$X > $PeirceQuincuncialLimit} {
00972     set X [expr {$PeirceQuincuncialScale - $X}]
00973     set southern 1
00974     } elseif {$Y < -$PeirceQuincuncialLimit} {
00975     set Y [expr {-$PeirceQuincuncialScale - $Y}]
00976     set southern 1
00977     } elseif {$Y > $PeirceQuincuncialLimit} {
00978     set Y [expr {$PeirceQuincuncialScale - $Y}]
00979     set southern 1
00980     }
00981 
00982     # Now we know that latitude will be positive.  If X is negative, then
00983     # longitude will be negative; reflect the Western Hemisphere into the
00984     # Eastern.
00985 
00986     set western 0
00987     if {$X < 0.0} {
00988     set western 1
00989     set X [expr {-$X}]
00990     }
00991 
00992     # If Y is positive, the point is in the back hemisphere.  Reflect
00993     # it to the front.
00994 
00995     set back 0
00996     if {$Y > 0.0} {
00997     set back 1
00998     set Y [expr {-$Y}]
00999     }
01000 
01001     # Finally, constrain longitude to be less than pi/4, by reflecting across
01002     # the 45 degree meridian.
01003 
01004     set complement 0
01005     if {$X > -$Y} {
01006     set complement 1
01007     set t [expr {-$X}]
01008     set X [expr {-$Y}]
01009     set Y $t
01010     }
01011 
01012     # Compute the elliptic functions to map the plane onto the sphere
01013 
01014     set cnx [cn $X $halfSqrt2]
01015     set cny [cn $Y $halfSqrt2]
01016 
01017     # Undo the mapping to latitude and longitude
01018 
01019     set a1 [expr {acos(-$cnx * $cnx)}]
01020     set a2 [expr {acos($cny * $cny)}]
01021     set b [expr {0.5 * ($a1 + $a2)}]
01022     set a [expr {0.5 * ($a1 - $a2)}]
01023     set cos_a [expr {cos($a)}]
01024     set cos_b [expr {-cos($b)}]
01025     set lambda [expr {$quarterpi - atan2($cos_b, $cos_a)}]
01026     set phi [expr {acos(hypot($cos_b, $cos_a))}]
01027 
01028     # Undo the reflections that were done above, to get correct latitude
01029     # and longitude
01030 
01031     if {$complement} {
01032     set lambda [expr {$halfpi - $lambda}]
01033     }
01034     if {$back} {
01035     set lambda [expr {$pi - $lambda}]
01036     }
01037     if {$western} {
01038     set lambda [expr {-$lambda}]
01039     }
01040     if {$southern} {
01041     set phi [expr {-$phi}]
01042     }
01043 
01044     # Convert latitude and longitude to degrees
01045 
01046     set lambda [expr {$lambda * $radian + 180. + $lambda_0}]
01047     if {$lambda < 0.0 || $lambda > 360.0} {
01048     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01049     }
01050     set lambda [expr {$lambda - 180.}]
01051     set phi [expr {$phi * $radian}]
01052 
01053     return [list $lambda $phi]
01054 }
01055 
01056 /*  ::mapproj::RotateCartesianY*/
01057 /* */
01058 /*  Rotates Cartesian co-ordinates about the y axis*/
01059 /* */
01060 /*  Parameters:*/
01061 /*  phi - Angle (in degrees) about which to rotate.*/
01062 /*  x,y,z - Cartesian co-ordinates*/
01063 /* */
01064 /*  Results:*/
01065 /*  Returns a three-element list giving the rotated co-ordinates.*/
01066 
01067 ret  ::mapproj::RotateCartesianY (type phi , type x , type y , type z) {
01068     variable degree
01069     set phi [expr {$degree * $phi}]
01070     set cos_phi [expr {cos($phi)}]
01071     set sin_phi [expr {sin($phi)}]
01072     return [list [expr {$x * $cos_phi - $z * $sin_phi}] \
01073         $y \
01074         [expr {$z * $cos_phi + $x * $sin_phi}]]
01075 }
01076 
01077 /*  ::mapproj::ToCartesian --*/
01078 /* */
01079 /*  Converts geodetic co-ordinates to Cartesian*/
01080 /* */
01081 /*  Parameters:*/
01082 /*  lambda - Longitude of the point to be projected, in degrees*/
01083 /*  phi - Latitude of the point to be projected, in degrees*/
01084 /* */
01085 /*  Results:*/
01086 /*  Returns a three-element list, x, y, z where x is the component*/
01087 /*  in the direction of longitude 0, latitude 0, y is the component*/
01088 /*  in the direction of longitude 90 East, latitude 0, and*/
01089 /*  z is the component in the direction of the North Pole*/
01090 /* */
01091 /*  Auxiliary procedure used in several projections to convert*/
01092 /*  geodetic coordinates to Cartesian range and bearing.*/
01093 
01094 ret  ::mapproj::ToCartesian (type lambda , type phi) {
01095     variable degree
01096     set lambda [expr {$degree * $lambda}]
01097     set phi [expr {$degree * $phi}]
01098     set cos_phi [expr cos($phi)]
01099     return [list [expr {$cos_phi * cos($lambda)}] \
01100         [expr {$cos_phi * sin($lambda)}] \
01101         [expr {sin($phi)}]]
01102 }
01103 
01104 /*  ::mapproj::CartesianToRangeAndBearing*/
01105 /* */
01106 /*  Transforms view-relative Cartesian co-ordinates to range and*/
01107 /*  bearing.*/
01108 /* */
01109 /*  Parameters:*/
01110 /*  x,y,z - Cartesian co-ordinates relative to center of Earth;*/
01111 /*  +x points to the viewer and +z to the "view-up" direction.*/
01112 /* */
01113 /*  Results:*/
01114 /*  Returns a three-element list containing, in order,*/
01115 /*  the cosine (easting) of the bearing, the sine (northing) of the*/
01116 /*  bearing, and the range.*/
01117 
01118 ret  ::mapproj::CartesianToRangeAndBearing (type x , type y , type z) {
01119     set c [expr {hypot($z, $y)}]
01120     if {$c == 0} {
01121     set cos_b 1.0
01122     set sin_b 0.0
01123     } else {
01124     set cos_b [expr {$y / $c}]
01125     set sin_b [expr {$z / $c}]
01126     }
01127     set range [expr {atan2($c, $x)}]
01128     return [list $cos_b $sin_b $range]
01129 }
01130 
01131 /*  ::mapproj::RangeAndBearingToCartesian --*/
01132 /* */
01133 /*  Converts range and bearing to Cartesian co-ordinates.*/
01134 /* */
01135 /*  Parameters:*/
01136 /*  cos_b, sin_b -- Cosine (easting) and sine (northing) of the bearing*/
01137 /*  range - Range, in Earth radii*/
01138 /* */
01139 /*  Results:*/
01140 /*  Returns Cartesian co-ordinates relative to center of Earth.*/
01141 /*  x is toward the station, and z is "view up"*/
01142 
01143 ret  ::mapproj::RangeAndBearingToCartesian (type cos_, type b , type sin_, type b , type range) {
01144     set c [expr {sin($range)}]
01145     set x [expr {cos($range)}]
01146     set y [expr {$cos_b * $c}]
01147     set z [expr {$sin_b * $c}]
01148     return [list $x $y $z]
01149 }
01150     
01151 
01152 /*  ::mapproj::CartesianToSpherical --*/
01153 /* */
01154 /*  Transforms Cartesian x, y, z to spherical co-ordinates*/
01155 /* */
01156 /*  Parameters:*/
01157 /*  x, y, z -- Coordinates of a point on the surface of the Earth,*/
01158 /*             in Earth radii*/
01159 /* */
01160 /*  Results:*/
01161 /*  Returns a two-element list comprising longitude and latitude*/
01162 /*  in radians*/
01163 
01164 ret  ::mapproj::CartesianToSpherical (type x , type y , type z) {
01165     return [list [expr {atan2($y, $x)}] [expr {atan2($z, hypot($y, $x))}]]
01166 }
01167 
01168 /*  ::mapproj::toOrthographic --*/
01169 /* */
01170 /*  Transforms latitude and longitude to x and y co-ordinates*/
01171 /*  on an orthographic projection.  Scale is true only at the*/
01172 /*  point of projection.*/
01173 /* */
01174 /*  Parameters:*/
01175 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01176 /*             in degrees*/
01177 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01178 /*             in degrees*/
01179 /* */
01180 /*  Results:*/
01181 /*  Returns map x and y co-ordinates, in Earth radii.*/
01182 
01183 ret  ::mapproj::toOrthographic (type lambda_0 , type phi_0 , type lambda , type phi) {
01184     foreach {x y z} [ToCartesian [expr {$lambda-$lambda_0}] $phi] \
01185     break
01186     foreach {x y z} [RotateCartesianY [expr {-$phi_0}] $x $y $z] \
01187     break
01188     if {$x < 0} {
01189     return {}
01190     } else {
01191     return [list $y $z]
01192     }
01193 }
01194 
01195 /*  ::mapproj::fromOrthographic --*/
01196 /* */
01197 /*  Transforms x and y on an orthographic projection to latitude*/
01198 /*  and longitude.*/
01199 /* */
01200 /*  Parameters:*/
01201 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01202 /*             in degrees*/
01203 /*  x, y -- Co-ordinates of the projected point, in Earth radii*/
01204 /* */
01205 /*  Results:*/
01206 /*  Returns a two element list containing longitude and latitude*/
01207 /*  in degrees.*/
01208 
01209 ret  ::mapproj::fromOrthographic (type lambda_0 , type phi_0 , type x , type y) {
01210     variable radian
01211     set r [expr {hypot($x, $y)}]
01212     set alpha [expr {asin($r)}]
01213     set z [expr {sqrt(1.0 - $r*$r)}]
01214     foreach {x y z} [RotateCartesianY $phi_0 $z $x $y] break
01215     foreach {lambda phi} [CartesianToSpherical $x $y $z] break
01216 
01217     # Convert latitude and longitude to degrees
01218 
01219     set lambda [expr {$lambda * $radian + 180. + $lambda_0}]
01220     if {$lambda < 0.0 || $lambda > 360.0} {
01221     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01222     }
01223     set lambda [expr {$lambda - 180.}]
01224     set phi [expr {$phi * $radian}]
01225 
01226     return [list $lambda $phi]
01227 }
01228 
01229 /*  ::mapproj::toStereographic --*/
01230 /* */
01231 /*  Transforms latitude and longitude to x and y co-ordinates*/
01232 /*  on an orthographic projection.  Scale is true only at the*/
01233 /*  point of projection.*/
01234 /* */
01235 /*  Parameters:*/
01236 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01237 /*             in degrees*/
01238 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01239 /*             in degrees*/
01240 /* */
01241 /*  Results:*/
01242 /*  Returns map x and y co-ordinates, in Earth radii.*/
01243 
01244 ret  ::mapproj::toStereographic (type lambda_0 , type phi_0 , type lambda , type phi) {
01245     foreach {x y z} [ToCartesian [expr {$lambda-$lambda_0}] $phi] \
01246     break
01247     foreach {x y z} [RotateCartesianY [expr {-$phi_0}] $x $y $z] \
01248     break
01249     if {$x < -0.5} {
01250     return {}
01251     } else {
01252     set y [expr {2. * $y / (1. + $x)}]
01253     set z [expr {2. * $z / (1. + $x)}]
01254     return [list $y $z]
01255     }
01256 }
01257 
01258 /*  ::mapproj::fromStereographic --*/
01259 /* */
01260 /*  Transforms x and y on an orthographic projection to latitude*/
01261 /*  and longitude.*/
01262 /* */
01263 /*  Parameters:*/
01264 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01265 /*             in degrees*/
01266 /*  x, y -- Co-ordinates of the projected point, in Earth radii*/
01267 /* */
01268 /*  Results:*/
01269 /*  Returns a two element list containing longitude and latitude*/
01270 /*  in degrees.*/
01271 
01272 ret  ::mapproj::fromStereographic (type lambda_0 , type phi_0 , type x , type y) {
01273     variable radian
01274     variable halfpi
01275     set denom [expr {4.0 + $x*$x + $y*$y}]
01276     foreach {x y z} [list \
01277              [expr {(4.0 - $x*$x - $y*$y) / $denom}] \
01278              [expr {4. * $x / $denom}] \
01279              [expr {4. * $y / $denom}]] break
01280     
01281     foreach {x y z} [RotateCartesianY $phi_0 $x $y $z] break
01282     foreach {lambda phi} [CartesianToSpherical $x $y $z] break
01283 
01284     # Convert latitude and longitude to degrees
01285 
01286     set lambda [expr {$lambda * $radian + 180. + $lambda_0}]
01287     if {$lambda < 0.0 || $lambda > 360.0} {
01288     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01289     }
01290     set lambda [expr {$lambda - 180.}]
01291     set phi [expr {$phi * $radian}]
01292 
01293     return [list $lambda $phi]
01294 }
01295 
01296 /*  ::mapproj::toGnomonic --*/
01297 /* */
01298 /*  Transforms latitude and longitude to x and y co-ordinates*/
01299 /*  on an orthographic projection.  Scale is true only at the*/
01300 /*  point of projection.*/
01301 /* */
01302 /*  Parameters:*/
01303 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01304 /*             in degrees*/
01305 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01306 /*             in degrees*/
01307 /* */
01308 /*  Results:*/
01309 /*  Returns map x and y co-ordinates, in Earth radii.*/
01310 
01311 ret  ::mapproj::toGnomonic (type lambda_0 , type phi_0 , type lambda , type phi) {
01312     foreach {x y z} [ToCartesian [expr {$lambda-$lambda_0}] $phi] \
01313     break
01314     foreach {x y z} [RotateCartesianY [expr {-$phi_0}] $x $y $z] \
01315     break
01316     if {$x < 0.01} {
01317     return {}
01318     } else {
01319     set y [expr {$y / $x}]
01320     set z [expr {$z / $x}]
01321     return [list $y $z]
01322     }
01323 }
01324 
01325 /*  ::mapproj::fromGnomonic --*/
01326 /* */
01327 /*  Transforms x and y on an orthographic projection to latitude*/
01328 /*  and longitude.*/
01329 /* */
01330 /*  Parameters:*/
01331 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01332 /*             in degrees*/
01333 /*  x, y -- Co-ordinates of the projected point, in Earth radii*/
01334 /* */
01335 /*  Results:*/
01336 /*  Returns a two element list containing longitude and latitude*/
01337 /*  in degrees.*/
01338 
01339 ret  ::mapproj::fromGnomonic (type lambda_0 , type phi_0 , type x , type y) {
01340     variable radian
01341     variable halfpi
01342     set denom [expr {hypot(1.0, hypot($x, $y))}]
01343     foreach {x y z} [list \
01344              [expr {1.0 / $denom}] \
01345              [expr {$x / $denom}] \
01346              [expr {$y / $denom}]] break
01347     
01348     foreach {x y z} [RotateCartesianY $phi_0 $x $y $z] break
01349     foreach {lambda phi} [CartesianToSpherical $x $y $z] break
01350 
01351     # Convert latitude and longitude to degrees
01352 
01353     set lambda [expr {$lambda * $radian + 180. + $lambda_0}]
01354     if {$lambda < 0.0 || $lambda > 360.0} {
01355     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01356     }
01357     set lambda [expr {$lambda - 180.}]
01358     set phi [expr {$phi * $radian}]
01359 
01360     return [list $lambda $phi]
01361 }
01362 
01363 /*  ::mapproj::toAzimuthalEquidistant --*/
01364 /* */
01365 /*  Transforms latitude and longitude to x and y co-ordinates*/
01366 /*  on an orthographic projection.  Scale is true only at the*/
01367 /*  point of projection.*/
01368 /* */
01369 /*  Parameters:*/
01370 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01371 /*             in degrees*/
01372 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01373 /*             in degrees*/
01374 /* */
01375 /*  Results:*/
01376 /*  Returns map x and y co-ordinates, in Earth radii.*/
01377 
01378 ret  ::mapproj::toAzimuthalEquidistant (type lambda_0 , type phi_0 , type lambda , type phi) {
01379     foreach {x y z} [ToCartesian [expr {$lambda-$lambda_0}] $phi] \
01380     break
01381     foreach {x y z} [RotateCartesianY [expr {-$phi_0}] $x $y $z] \
01382     break
01383     foreach {cs sn range} [CartesianToRangeAndBearing $x $y $z] break
01384     return [list [expr {$cs * $range}] [expr {$sn * $range}]]
01385 }
01386 
01387 /*  ::mapproj::fromAzimuthalEquidistant --*/
01388 /* */
01389 /*  Transforms x and y on an orthographic projection to latitude*/
01390 /*  and longitude.*/
01391 /* */
01392 /*  Parameters:*/
01393 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01394 /*             in degrees*/
01395 /*  x, y -- Co-ordinates of the projected point, in Earth radii*/
01396 /* */
01397 /*  Results:*/
01398 /*  Returns a two element list containing longitude and latitude*/
01399 /*  in degrees.*/
01400 
01401 ret  ::mapproj::fromAzimuthalEquidistant (type lambda_0 , type phi_0 , type x , type y) {
01402     variable radian
01403     variable halfpi
01404 
01405     set range [expr {hypot($y, $x)}]
01406     set cos_b [expr {$x / $range}]
01407     set sin_b [expr {$y / $range}]
01408     foreach {x y z} [RangeAndBearingToCartesian $cos_b $sin_b $range] break
01409     foreach {x y z} [RotateCartesianY $phi_0 $x $y $z] break
01410     foreach {lambda phi} [CartesianToSpherical $x $y $z] break
01411 
01412     # Convert latitude and longitude to degrees
01413 
01414     set lambda [expr {$lambda * $radian + 180. + $lambda_0}]
01415     if {$lambda < 0.0 || $lambda > 360.0} {
01416     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01417     }
01418     set lambda [expr {$lambda - 180.}]
01419     set phi [expr {$phi * $radian}]
01420 
01421     return [list $lambda $phi]
01422 }
01423 
01424 /*  ::mapproj::toLambertAzimuthalEqualArea --*/
01425 /* */
01426 /*  Transforms latitude and longitude to x and y co-ordinates*/
01427 /*  on an orthographic projection.  Scale is true only at the*/
01428 /*  point of projection.*/
01429 /* */
01430 /*  Parameters:*/
01431 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01432 /*             in degrees*/
01433 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01434 /*             in degrees*/
01435 /* */
01436 /*  Results:*/
01437 /*  Returns map x and y co-ordinates, in Earth radii.*/
01438 
01439 ret  ::mapproj::toLambertAzimuthalEqualArea (type lambda_0 , type phi_0 , type lambda , type phi) {
01440     foreach {x y z} [ToCartesian [expr {$lambda-$lambda_0}] $phi] \
01441     break
01442     foreach {x y z} [RotateCartesianY [expr {-$phi_0}] $x $y $z] \
01443     break
01444     foreach {cs sn range} [CartesianToRangeAndBearing $x $y $z] break
01445     set range [expr {2.0 * sin(0.5 * $range)}]
01446     return [list [expr {$cs * $range}] [expr {$sn * $range}]]
01447 }
01448 
01449 /*  ::mapproj::fromLambertAzimuthalEqualArea --*/
01450 /* */
01451 /*  Transforms x and y on an orthographic projection to latitude*/
01452 /*  and longitude.*/
01453 /* */
01454 /*  Parameters:*/
01455 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01456 /*             in degrees*/
01457 /*  x, y -- Co-ordinates of the projected point, in Earth radii*/
01458 /* */
01459 /*  Results:*/
01460 /*  Returns a two element list containing longitude and latitude*/
01461 /*  in degrees.*/
01462 
01463 ret  ::mapproj::fromLambertAzimuthalEqualArea (type lambda_0 , type phi_0 , type x , type y) {
01464     variable radian
01465     variable halfpi
01466 
01467     set range [expr {hypot($y, $x)}]
01468     set cos_b [expr {$x / $range}]
01469     set sin_b [expr {$y / $range}]
01470     set range [expr {2.0 * asin(0.5 * $range)}]
01471     foreach {x y z} [RangeAndBearingToCartesian $cos_b $sin_b $range] break
01472     foreach {x y z} [RotateCartesianY $phi_0 $x $y $z] break
01473     foreach {lambda phi} [CartesianToSpherical $x $y $z] break
01474 
01475     # Convert latitude and longitude to degrees
01476 
01477     set lambda [expr {$lambda * $radian + 180. + $lambda_0}]
01478     if {$lambda < 0.0 || $lambda > 360.0} {
01479     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01480     }
01481     set lambda [expr {$lambda - 180.}]
01482     set phi [expr {$phi * $radian}]
01483 
01484     return [list $lambda $phi]
01485 }
01486 
01487 /*  ::mapproj::toHammer --*/
01488 /* */
01489 /*  Transforms latitude and longitude to x and y co-ordinates*/
01490 /*  on an orthographic projection.  Scale is true only at the*/
01491 /*  point of projection.*/
01492 /* */
01493 /*  Parameters:*/
01494 /*  lambda_0-- Longitude of the center of projection*/
01495 /*             in degrees*/
01496 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01497 /*             in degrees*/
01498 /* */
01499 /*  Results:*/
01500 /*  Returns map x and y co-ordinates, in Earth radii.*/
01501 
01502 ret  ::mapproj::toHammer (type lambda_0 , type lambda , type phi) {
01503     set lambda [expr {$lambda - $lambda_0 + 180.}]
01504     if {$lambda < 0.0 || $lambda > 360.0} {
01505     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01506     }
01507     set lambda [expr {$lambda - 180.0}]
01508     foreach {x y z} [ToCartesian [expr {$lambda/2.}] $phi] \
01509     break
01510     foreach {cs sn range} [CartesianToRangeAndBearing $x $y $z] break
01511     set range [expr {2.0 * sin(0.5 * $range)}]
01512     return [list [expr {2.0 * $cs * $range}] [expr {$sn * $range}]]
01513 }
01514 
01515 /*  ::mapproj::fromHammer --*/
01516 /* */
01517 /*  Transforms x and y on an orthographic projection to latitude*/
01518 /*  and longitude.*/
01519 /* */
01520 /*  Parameters:*/
01521 /*  lambda_0, phi_0 -- Longitude and latitude of the center of projection*/
01522 /*             in degrees*/
01523 /*  x, y -- Co-ordinates of the projected point, in Earth radii*/
01524 /* */
01525 /*  Results:*/
01526 /*  Returns a two element list containing longitude and latitude*/
01527 /*  in degrees.*/
01528 
01529 ret  ::mapproj::fromHammer (type lambda_0 , type x , type y) {
01530     variable radian
01531     variable halfpi
01532 
01533     set x [expr {0.5 * $x}]
01534     set range [expr {hypot($y, $x)}]
01535     set cos_b [expr {$x / $range}]
01536     set sin_b [expr {$y / $range}]
01537     set range [expr {2.0 * asin(0.5 * $range)}]
01538     foreach {x y z} [RangeAndBearingToCartesian $cos_b $sin_b $range] break
01539     foreach {lambda phi} [CartesianToSpherical $x $y $z] break
01540 
01541     # Convert latitude and longitude to degrees
01542 
01543     set lambda [expr {2.0 * $lambda * $radian + 180. + $lambda_0}]
01544     if {$lambda < 0.0 || $lambda > 360.0} {
01545     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01546     }
01547     set lambda [expr {$lambda - 180.}]
01548     set phi [expr {$phi * $radian}]
01549 
01550     return [list $lambda $phi]
01551 }
01552 
01553 /*  ::mapproj::toConicEquidistant*/
01554 /* */
01555 /*  Converts latitude and longitude to map co-ordinates on a*/
01556 /*  conic equidistant projection.*/
01557 /* */
01558 /*  Parameters:*/
01559 /*  lambda_0, phi_0 -- Longitude and latitude of the center of the sheet.*/
01560 /*  phi_1, phi_2 -- Latitudes of the two standard parallels at which scale*/
01561 /*          is true*/
01562 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01563 /* */
01564 /*  Results:*/
01565 /*  Returns a list of map x and y measured in Earth radii.*/
01566 
01567 ret  ::mapproj::toConicEquidistant (type lambda_0 , type phi_0 , type phi_1 , type phi_2 , type lambda , type phi) {
01568     variable degree
01569     set phi_0 [expr {$phi_0 * $degree}]
01570     set phi_1 [expr {$phi_1 * $degree}]
01571     set phi_2 [expr {$phi_2 * $degree}]
01572     set lambda [expr {$lambda - $lambda_0 + 180.}]
01573     if {$lambda < 0.0 || $lambda > 360.0} {
01574     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01575     }
01576     set lambda [expr {($lambda - 180.0) * $degree}]
01577     set phi [expr {$phi * $degree}]
01578     set cos_phi_1 [expr {cos($phi_1)}]
01579     set n [expr {($cos_phi_1 - cos($phi_2)) / ($phi_2 - $phi_1)}]
01580     set G [expr {$cos_phi_1 / $n + $phi_1}]
01581     set rho_0 [expr {$G - $phi_0}]
01582     set theta [expr {$n * $lambda}]
01583     set rho [expr {$G - $phi}]
01584     set x [expr {$rho * sin($theta)}]
01585     set y [expr {$rho_0 - $rho * cos($theta)}]
01586     return [list $x $y]
01587 }
01588 
01589 /*  ::mapproj::fromConicEquidistant --*/
01590 /* */
01591 /*  Unprojects map x and y in a conic equidistant projection to*/
01592 /*  latitude and longitude*/
01593 /* */
01594 /*  Parameters:*/
01595 /*  lambda_0, phi_0 -- Longitude and latitude of the center of the sheet.*/
01596 /*  phi_1, phi_2 -- Latitudes of the two standard parallels at which scale*/
01597 /*          is true*/
01598 /*  x, y -- Map co-ordinates in Earth radii*/
01599 /* */
01600 /*  Results:*/
01601 /*  Returns a list of longitude and latitude in degrees.*/
01602 
01603 ret  ::mapproj::fromConicEquidistant (type lambda_0 , type phi_0 , type phi_1 , type phi_2 , type x , type y) {
01604     variable degree
01605     variable radian
01606     set phi_0 [expr {$phi_0 * $degree}]
01607     set phi_1 [expr {$phi_1 * $degree}]
01608     set phi_2 [expr {$phi_2 * $degree}]
01609     set cos_phi_1 [expr {cos($phi_1)}]
01610     set n [expr {($cos_phi_1 - cos($phi_2)) / ($phi_2 - $phi_1)}]
01611     set G [expr {$cos_phi_1 / $n + $phi_1}]
01612     set rho_0 [expr {$G - $phi_0}]
01613     set rho_0my [expr {$rho_0 - $y}]
01614     set theta [expr {atan2($x, $rho_0my)}]
01615     set rho [expr {sqrt($x*$x + $rho_0my * $rho_0my)}]
01616     if {$n < 0.0} {set rho [expr {-$rho}]}
01617     set phi [expr {($G - $rho) * $radian}]
01618     set lambda [expr {($theta / $n * $radian) + $lambda_0 + 180.}]
01619     if {$lambda < 0.0 || $lambda > 360.0} {
01620     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01621     }
01622     set lambda [expr {$lambda - 180.}]
01623     return [list $lambda $phi]
01624 }
01625 
01626 /*  ::mapproj::toAlbersEqualAreaConic*/
01627 /* */
01628 /*  Converts latitude and longitude to map co-ordinates on a*/
01629 /*  conic equal-area projection.*/
01630 /* */
01631 /*  Parameters:*/
01632 /*  lambda_0, phi_0 -- Longitude and latitude of the center of the sheet.*/
01633 /*  phi_1, phi_2 -- Latitudes of the two standard parallels at which scale*/
01634 /*          is true*/
01635 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01636 /* */
01637 /*  Results:*/
01638 /*  Returns a list of map x and y measured in Earth radii.*/
01639 
01640 ret  ::mapproj::toAlbersEqualAreaConic (type lambda_0 , type phi_0 , type phi_1 , type phi_2 , type lambda , type phi) {
01641     variable degree
01642     set phi_0 [expr {$phi_0 * $degree}]
01643     set phi_1 [expr {$phi_1 * $degree}]
01644     set phi_2 [expr {$phi_2 * $degree}]
01645     set lambda [expr {$lambda - $lambda_0 + 180.}]
01646     if {$lambda < 0.0 || $lambda > 360.0} {
01647     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01648     }
01649     set lambda [expr {($lambda - 180.0) * $degree}]
01650     set phi [expr {$phi * $degree}]
01651     set cos_phi_1 [expr {cos($phi_1)}]
01652     set sin_phi_1 [expr {sin($phi_1)}]
01653     set n [expr {0.5 * ($sin_phi_1 + sin($phi_2))}]
01654     set theta [expr {$n * $lambda}]
01655     set C [expr {$cos_phi_1 * $cos_phi_1 + 2.0 * $n * $sin_phi_1}]
01656     set rho [expr {sqrt($C - 2.0 * $n * sin($phi)) / $n}]
01657     set rho_0 [expr {sqrt($C - 2.0 * $n * sin($phi_0)) / $n}]
01658     set x [expr {$rho * sin($theta)}]
01659     set y [expr {$rho_0 - $rho * cos($theta)}]
01660     return [list $x $y]
01661 }
01662 
01663 /*  ::mapproj::fromAlbersEqualAreaConic --*/
01664 /* */
01665 /*  Unprojects map x and y in a conic equal-area projection to*/
01666 /*  latitude and longitude*/
01667 /* */
01668 /*  Parameters:*/
01669 /*  lambda_0, phi_0 -- Longitude and latitude of the center of the sheet.*/
01670 /*  phi_1, phi_2 -- Latitudes of the two standard parallels at which scale*/
01671 /*          is true*/
01672 /*  x, y -- Map co-ordinates in Earth radii*/
01673 /* */
01674 /*  Results:*/
01675 /*  Returns a list of longitude and latitude in degrees.*/
01676 
01677 ret  ::mapproj::fromAlbersEqualAreaConic (type lambda_0 , type phi_0 , type phi_1 , type phi_2 , type x , type y) {
01678     variable degree
01679     variable radian
01680     variable twopi
01681     set phi_0 [expr {$phi_0 * $degree}]
01682     set phi_1 [expr {$phi_1 * $degree}]
01683     set phi_2 [expr {$phi_2 * $degree}]
01684     set cos_phi_1 [expr {cos($phi_1)}]
01685     set sin_phi_1 [expr {sin($phi_1)}]
01686     set n [expr {0.5 * ($sin_phi_1 + sin($phi_2))}]
01687     set C [expr {$cos_phi_1 * $cos_phi_1 + 2.0 * $n * $sin_phi_1}]
01688     set rho_0 [expr {sqrt($C - 2.0 * $n * sin($phi_0)) / $n}]
01689     set theta [expr {atan2($x, $rho_0 - $y)}]
01690     set rho [expr {hypot($x, $rho_0 - $y)}]
01691     set phi [expr {$radian * asin(($C - $rho*$rho*$n*$n) / (2.0 * $n))}]
01692     set lambda [expr {($theta / $n * $radian) + $lambda_0 + 180.}]
01693     if {$lambda < 0.0 || $lambda > 360.0} {
01694     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01695     }
01696     set lambda [expr {$lambda - 180.}]
01697     return [list $lambda $phi]
01698 }
01699 
01700 /*  ::mapproj::toLambertConformalConic*/
01701 /* */
01702 /*  Converts latitude and longitude to map co-ordinates on a*/
01703 /*  conformal conic projection.*/
01704 /* */
01705 /*  Parameters:*/
01706 /*  lambda_0, phi_0 -- Longitude and latitude of the center of the sheet.*/
01707 /*  phi_1, phi_2 -- Latitudes of the two standard parallels at which scale*/
01708 /*          is true*/
01709 /*  lambda, phi -- Longitude and latitude of the point to be projected*/
01710 /* */
01711 /*  Results:*/
01712 /*  Returns a list of map x and y measured in Earth radii.*/
01713 
01714 ret  ::mapproj::toLambertConformalConic (type lambda_0 , type phi_0 , type phi_1 , type phi_2 , type lambda , type phi) {
01715     variable degree
01716     variable quarterpi
01717     set phi_0 [expr {$phi_0 * $degree}]
01718     set phi_1 [expr {$phi_1 * $degree}]
01719     set phi_2 [expr {$phi_2 * $degree}]
01720     set lambda [expr {$lambda - $lambda_0 + 180.}]
01721     if {$lambda < 0.0 || $lambda > 360.0} {
01722     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01723     }
01724     set lambda [expr {($lambda - 180.0) * $degree}]
01725     set phi [expr {$phi * $degree}]
01726     set cos_phi_1 [expr {cos($phi_1)}]
01727     set sin_phi_1 [expr {sin($phi_1)}]
01728     set tan1 [expr {tan($quarterpi + 0.5 * $phi_1)}]
01729     set n [expr {log($cos_phi_1 / cos($phi_2))
01730          / log(tan($quarterpi + 0.5 * $phi_2) / $tan1)}]
01731     set F [expr {$cos_phi_1 * pow($tan1, $n) / $n}]
01732     set rho [expr {$F * pow(tan($quarterpi + 0.5 * $phi), -$n)}]
01733     set rho_0 [expr {$F * pow(tan($quarterpi + 0.5 * $phi_0), -$n)}]
01734     set x [expr {$rho * sin($n * $lambda)}]
01735     set y [expr {$rho_0 - $rho * cos($n * $lambda)}]
01736     return [list $x $y]
01737 }
01738 
01739 /*  ::mapproj::fromLambertConformalConic --*/
01740 /* */
01741 /*  Unprojects map x and y in a conformal conic projection to*/
01742 /*  latitude and longitude*/
01743 /* */
01744 /*  Parameters:*/
01745 /*  lambda_0, phi_0 -- Longitude and latitude of the center of the sheet.*/
01746 /*  phi_1, phi_2 -- Latitudes of the two standard parallels at which scale*/
01747 /*          is true*/
01748 /*  x, y -- Map co-ordinates in Earth radii*/
01749 /* */
01750 /*  Results:*/
01751 /*  Returns a list of longitude and latitude in degrees.*/
01752 
01753 ret  ::mapproj::fromLambertConformalConic (type lambda_0 , type phi_0 , type phi_1 , type phi_2 , type x , type y) {
01754     variable degree
01755     variable radian
01756     variable quarterpi
01757     set phi_0 [expr {$phi_0 * $degree}]
01758     set phi_1 [expr {$phi_1 * $degree}]
01759     set phi_2 [expr {$phi_2 * $degree}]
01760     set cos_phi_1 [expr {cos($phi_1)}]
01761     set sin_phi_1 [expr {sin($phi_1)}]
01762     set tan1 [expr {tan($quarterpi + 0.5 * $phi_1)}]
01763     set n [expr {log($cos_phi_1 / cos($phi_2))
01764          / log(tan($quarterpi + 0.5 * $phi_2) / $tan1)}]
01765     set F [expr {$cos_phi_1 * pow($tan1, $n) / $n}]
01766     set rho_0 [expr {$F * pow(tan($quarterpi + 0.5 * $phi_0), -$n)}]
01767     set y [expr {$rho_0 - $y}]
01768     set rho [expr {sqrt($x*$x + $y*$y)}]
01769     if {$n < 0} { set rho [expr {-$rho}] }
01770     set theta [expr {atan2($x, $y)}]
01771     set phi [expr {$radian * 2 * atan(pow($F / $rho, 1.0 / $n)) - 90.}]
01772     set lambda [expr {($theta / $n * $radian) + $lambda_0 + 180.}]
01773     if {$lambda < 0.0 || $lambda > 360.0} {
01774     set lambda [expr {$lambda - 360. * floor($lambda / 360.)}]
01775     }
01776     set lambda [expr {$lambda - 180.}]
01777     return [list $lambda $phi]
01778 }
01779 
01780 /*  Define commonly used cylindrical equal-area projections*/
01781 
01782 ret  ::mapproj::toLambertCylindricalEqualArea (type lambda_0 , type phi_0 , type lambda , type phi) {
01783     toCylindricalEqualArea 0.0 $lambda_0 $phi_0 $lambda $phi
01784 }
01785 ret  ::mapproj::fromLambertCylindricalEqualArea (type lambda_0 , type phi_0 , type x , type y) {
01786     fromCylindricalEqualArea 0.0 $lambda_0 $phi_0 $x $y
01787 }
01788 ret  ::mapproj::toBehrmann (type lambda_0 , type phi_0 , type lambda , type phi) {
01789     toCylindricalEqualArea 30.0 $lambda_0 $phi_0 $lambda $phi
01790 }
01791 ret  ::mapproj::fromBehrmann (type lambda_0 , type phi_0 , type x , type y) {
01792     fromCylindricalEqualArea 30.0 $lambda_0 $phi_0 $x $y
01793 }
01794 ret  ::mapproj::toTrystanEdwards (type lambda_0 , type phi_0 , type lambda , type phi) {
01795     toCylindricalEqualArea 37.4 $lambda_0 $phi_0 $lambda $phi
01796 }
01797 ret  ::mapproj::fromTrystanEdwards (type lambda_0 , type phi_0 , type x , type y) {
01798     fromCylindricalEqualArea 37.4 $lambda_0 $phi_0 $x $y
01799 }
01800 ret  ::mapproj::toHoboDyer (type lambda_0 , type phi_0 , type lambda , type phi) {
01801     toCylindricalEqualArea 37.5 $lambda_0 $phi_0 $lambda $phi
01802 }
01803 ret  ::mapproj::fromHoboDyer (type lambda_0 , type phi_0 , type x , type y) {
01804     fromCylindricalEqualArea 37.5 $lambda_0 $phi_0 $x $y
01805 }
01806 ret  ::mapproj::toGallPeters (type lambda_0 , type phi_0 , type lambda , type phi) {
01807     toCylindricalEqualArea 45.0 $lambda_0 $phi_0 $lambda $phi
01808 }
01809 ret  ::mapproj::fromGallPeters (type lambda_0 , type phi_0 , type x , type y) {
01810     fromCylindricalEqualArea 45.0 $lambda_0 $phi_0 $x $y
01811 }
01812 ret  ::mapproj::toBalthasart (type lambda_0 , type phi_0 , type lambda , type phi) {
01813     toCylindricalEqualArea 50.0 $lambda_0 $phi_0 $lambda $phi
01814 }
01815 ret  ::mapproj::fromBalthasart (type lambda_0 , type phi_0 , type x , type y) {
01816     fromCylindricalEqualArea 50.0 $lambda_0 $phi_0 $x $y
01817 }
01818 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1