Changeset bc386f0 in sasmodels for explore


Ignore:
Timestamp:
Nov 1, 2017 11:47:41 AM (7 years ago)
Author:
Paul Kienzle <pkienzle@…>
Branches:
master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
05e4f8a
Parents:
0f65169
Message:

guyou code cleanup

File:
1 edited

Legend:

Unmodified
Added
Removed
  • explore/guyou.py

    r0f65169 rbc386f0  
     1# Copyright 2013-2016 Mike Bostock 
     2# All rights reserved. 
     3# 
     4# Redistribution and use in source and binary forms, with or without modification, 
     5# are permitted provided that the following conditions are met: 
     6# 
     7# * Redistributions of source code must retain the above copyright notice, this 
     8#   list of conditions and the following disclaimer. 
     9# 
     10# * Redistributions in binary form must reproduce the above copyright notice, 
     11#   this list of conditions and the following disclaimer in the documentation 
     12#   and/or other materials provided with the distribution. 
     13# 
     14# * Neither the name of the author nor the names of contributors may be used to 
     15#   endorse or promote products derived from this software without specific prior 
     16#   written permission. 
     17# 
     18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
     19# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
     20# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
     21# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
     22# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
     23# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
     24# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
     25# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
     26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
     27# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
     28# 
     29# https://github.com/d3/d3-geo-projection 
     30# commit fd2886555e46b35163c7b898d43c7d1bcebbba7c 2016-07-02 
     31# 
     32# 2017-11-01 Paul Kienzle 
     33# * converted to python, using degrees rather than radians 
    134from __future__ import division, print_function 
    235 
     
    437from numpy import sqrt, pi, tan, cos, sin, log, exp, arctan2 as atan2, sign, radians, degrees 
    538 
     39_ = """ 
    640# mpmath version of special functions 
    7 _ = """ 
    841import mpmath as mp 
    942sn, cn, dn = [mp.ellipfun(v) for v in 'sn', 'cn', 'dn'] 
     
    84117    cotlambda2 = (-b + sqrt(b * b - 4 * c)) / 2 
    85118    re = ellipticF(atan(1 / sqrt(cotlambda2)), m) * sign(phi) 
    86     im = ellipticF(atan(sqrt((cotlambda2 / cotphi2 - 1) / m)), 1 - m) * sign(psi) 
     119    im = ellipticF(atan(sqrt(np.maximum(0,(cotlambda2 / cotphi2 - 1) / m))), 1 - m) * sign(psi) 
    87120    return re + 1j*im 
    88121 
     122sqrt2 = sqrt(2) 
     123 
     124# [PAK] renamed k_ => cos_u, k => sin_u, k*k => sinsq_u to avoid k,K confusion 
     125# cos_u = 0.171572875253809902396622551580603842860656249246103853646... 
     126# sinsq_u = 0.970562748477140585620264690516376942836062504523376878120... 
     127# K = 5.415790687177373152821133989303623814510776592635531270636... 
     128def guyou(lam, phi): 
     129    # [PAK] wrap into [-pi/2, pi/2] radians 
     130    x, y = np.asarray(lam), np.asarray(phi) 
     131    xn, x = divmod(x+90, 180) 
     132    yn, y = divmod(y+90, 180) 
     133    xn, lam = xn*180, radians(x-90) 
     134    yn, phi = yn*180, radians(y-90) 
     135 
     136    # Compute constant K 
     137    cos_u = (sqrt2 - 1) / (sqrt2 + 1) 
     138    sinsq_u = 1 - cos_u**2 
     139    K = ellipticF(pi/2, sinsq_u) 
     140 
     141    # [PAK] simplify expressions, using the fact that f = -1 
     142    # Note: exp(f log(x)) => 1/x,  cos(f x) => cos(x), sin(f x) => -sin(x) 
     143    r = 1/(tan(pi/4 + abs(phi)/2) * sqrt(cos_u)) 
     144    at = atan(r * (cos(lam) - 1j*sin(lam))) 
     145    t = ellipticFi(at.real, at.imag, sinsq_u) 
     146    x, y = (-t.imag, sign(phi + (phi==0))*(0.5 * K - t.real)) 
     147 
     148    # [PAK] convert to degrees, and return to original tile 
     149    return degrees(x)+xn, degrees(y)+yn 
     150 
     151def guyou_invert(x, y): 
     152    # [PAK] wrap into [-pi/2, pi/2] radians 
     153    x, y = np.asarray(x), np.asarray(y) 
     154    xn, x = divmod(x+90, 180) 
     155    yn, y = divmod(y+90, 180) 
     156    xn, x = xn*180, radians(x-90) 
     157    yn, y = yn*180, radians(y-90) 
     158 
     159    # compute constant K 
     160    cos_u = (sqrt2 - 1) / (sqrt2 + 1) 
     161    sinsq_u = 1 - cos_u**2 
     162    K = ellipticF(pi/2, sinsq_u) 
     163 
     164    # [PAK] simplify expressions, using the fact that f = -1 
     165    # Note: exp(0.5/f log(x)) => 1/sqrt(x), a x.real^2 + a x.imag^2 => a|x|^2 
     166    j = ellipticJi(0.5 * K - y, -x, sinsq_u) 
     167    tn = j[0]/j[1]  # j[0], j[1] are complex 
     168    lam = -atan2(tn.imag, tn.real) 
     169    phi = 2*atan(1/sqrt(cos_u*abs(tn)**2)) - pi/2 
     170 
     171    # [PAK] convert to degrees, and return to original tile 
     172    return degrees(lam)+xn, degrees(phi)+yn 
     173 
     174def plot_grid(): 
     175    import matplotlib.pyplot as plt 
     176    from numpy import linspace 
     177    lat_line = linspace(-90, 90, 400) 
     178    long_line = linspace(-90, 90, 400) 
     179 
     180    #scale = 1 
     181    limit, step, scale = 90, 10, 2 
     182    plt.subplot(211) 
     183    for lat in range(-limit, limit+1, step): 
     184        x, y = guyou(scale*lat_line, scale*lat) 
     185        plt.plot(x, y, 'g') 
     186 
     187    for long in range(-limit, limit+1, step): 
     188        x, y = guyou(scale*long, scale*long_line) 
     189        plt.plot(x, y, 'b') 
     190    #plt.xlabel('longitude') 
     191    plt.ylabel('latitude') 
     192 
     193    plt.subplot(212) 
     194    for lat in range(-limit, limit+1, step): 
     195        x, y = guyou_invert(scale*lat_line, scale*lat) 
     196        plt.plot(x, y, 'g') 
     197 
     198    for long in range(-limit, limit+1, step): 
     199        x, y = guyou_invert(scale*long, scale*long_line) 
     200        plt.plot(x, y, 'b') 
     201    plt.xlabel('longitude') 
     202    plt.ylabel('latitude') 
     203 
     204if __name__ == "__main__": 
     205    plot_grid() 
     206    import matplotlib.pyplot as plt; plt.show() 
     207 
     208 
    89209_ = """ 
     210// Javascript source for elliptic functions 
     211// 
    90212// Returns [sn, cn, dn](u + iv|m). 
    91213export function ellipticJi(u, v, m) { 
     
    203325  } 
    204326  return phi / (pow(2, i) * a); 
     327 
     328 
     329export function guyouRaw(lambda, phi) { 
     330  var k_ = (sqrt2 - 1) / (sqrt2 + 1), 
     331      k = sqrt(1 - k_ * k_), 
     332      K = ellipticF(halfPi, k * k), 
     333      f = -1, 
     334      psi = log(tan(pi / 4 + abs(phi) / 2)), 
     335      r = exp(f * psi) / sqrt(k_), 
     336      at = guyouComplexAtan(r * cos(f * lambda), r * sin(f * lambda)), 
     337      t = ellipticFi(at[0], at[1], k * k); 
     338  return [-t[1], (phi >= 0 ? 1 : -1) * (0.5 * K - t[0])]; 
     339} 
     340 
     341function guyouComplexAtan(x, y) { 
     342  var x2 = x * x, 
     343      y_1 = y + 1, 
     344      t = 1 - x2 - y * y; 
     345  return [ 
     346   0.5 * ((x >= 0 ? halfPi : -halfPi) - atan2(t, 2 * x)), 
     347    -0.25 * log(t * t + 4 * x2) +0.5 * log(y_1 * y_1 + x2) 
     348  ]; 
     349} 
     350 
     351function guyouComplexDivide(a, b) { 
     352  var denominator = b[0] * b[0] + b[1] * b[1]; 
     353  return [ 
     354    (a[0] * b[0] + a[1] * b[1]) / denominator, 
     355    (a[1] * b[0] - a[0] * b[1]) / denominator 
     356  ]; 
     357} 
     358 
     359guyouRaw.invert = function(x, y) { 
     360  var k_ = (sqrt2 - 1) / (sqrt2 + 1), 
     361      k = sqrt(1 - k_ * k_), 
     362      K = ellipticF(halfPi, k * k), 
     363      f = -1, 
     364      j = ellipticJi(0.5 * K - y, -x, k * k), 
     365      tn = guyouComplexDivide(j[0], j[1]), 
     366      lambda = atan2(tn[1], tn[0]) / f; 
     367  return [ 
     368    lambda, 
     369    2 * atan(exp(0.5 / f * log(k_ * tn[0] * tn[0] + k_ * tn[1] * tn[1]))) - halfPi 
     370  ]; 
     371}; 
    205372""" 
    206  
    207 def guyouComplexAtan(x, y): 
    208     x2 = x * x 
    209     y_1 = y + 1 
    210     t = 1 - x2 - y * y 
    211     return ( 
    212        0.5 * (sign(x+(x==0))*halfPi - atan2(t, 2 * x)), 
    213        -0.25 * log(t * t + 4 * x2) +0.5 * log(y_1 * y_1 + x2) 
    214     ) 
    215  
    216 sqrt2 = sqrt(2) 
    217 halfPi = pi/2 
    218  
    219 # From d3-geo-projections 
    220 def guyou(lam, phi): 
    221     x, y = np.asarray(lam), np.asarray(phi) 
    222     xn, x = divmod(x+90, 180) 
    223     yn, y = divmod(y+90, 180) 
    224     xn, lam = xn*180, radians(x-90) 
    225     yn, phi = yn*180, radians(y-90) 
    226  
    227     k_ = (sqrt2 - 1) / (sqrt2 + 1) 
    228     k = sqrt(1 - k_ * k_) 
    229     K = ellipticF(halfPi, k * k) 
    230     f = -1 
    231     psi = log(tan(pi / 4 + abs(phi) / 2)) 
    232     r = exp(f * psi) / sqrt(k_) 
    233     at = guyouComplexAtan(r * cos(f * lam), r * sin(f * lam)) 
    234     t = ellipticFi(at[0], at[1], k * k) 
    235     #return [-t.imag, (1 if phi >=0 else -1)*(0.5 * K - t.real)] 
    236     x, y = (-t.imag, sign(phi + (phi==0))*(0.5 * K - t.real)) 
    237     return degrees(x)+xn, degrees(y)+yn 
    238  
    239 #def guyouComplexDivide(a, b): 
    240 #    denominator = b[0] * b[0] + b[1] * b[1] 
    241 #    return [ 
    242 #        (a[0] * b[0] + a[1] * b[1]) / denominator, 
    243 #        (a[1] * b[0] - a[0] * b[1]) / denominator 
    244 #    ] 
    245  
    246 def guyou_invert(x, y): 
    247     x, y = np.asarray(x), np.asarray(y) 
    248     xn, x = divmod(x+90, 180) 
    249     yn, y = divmod(y+90, 180) 
    250     xn, x = xn*180, radians(x-90) 
    251     yn, y = yn*180, radians(y-90) 
    252  
    253     k_ = (sqrt2 - 1) / (sqrt2 + 1) 
    254     k = sqrt(1 - k_ * k_) 
    255     K = ellipticF(halfPi, k * k) 
    256     f = -1 
    257     j = ellipticJi(0.5 * K - y, -x, k * k) 
    258     #tn = guyouComplexDivide(j[0], j[1]) 
    259     #lam = atan2(tn[1], tn[0]) / f 
    260     tn = j[0]/j[1]  # j[0], j[1] are complex 
    261     lam = atan2(tn.imag, tn.real) / f 
    262     phi = 2 * atan(exp(0.5 / f * log(k_ * tn.real**2 + k_ * tn.imag**2))) - halfPi 
    263     return degrees(lam)+xn, degrees(phi)+yn 
    264  
    265 def plot_grid(): 
    266     import matplotlib.pyplot as plt 
    267     from numpy import linspace 
    268     lat_line = linspace(-90, 90, 100)[1:-1] 
    269     long_line = linspace(-90, 90, 100)[1:-1] 
    270  
    271     #scale = 1 
    272     scale = 2 
    273     plt.subplot(211) 
    274     for lat in range(-80, 81, 10): 
    275         x, y = guyou(scale*lat_line, scale*lat) 
    276         plt.plot(x, y, 'g') 
    277  
    278     for long in range(-80, 81, 10): 
    279         x, y = guyou(scale*long, scale*long_line) 
    280         plt.plot(x, y, 'b') 
    281     plt.subplot(212) 
    282     for lat in range(-80, 81, 10): 
    283         x, y = guyou_invert(scale*lat_line, scale*lat) 
    284         plt.plot(x, y, 'g') 
    285  
    286     for long in range(-80, 81, 10): 
    287         x, y = guyou_invert(scale*long, scale*long_line) 
    288         plt.plot(x, y, 'b') 
    289  
    290 if __name__ == "__main__": 
    291     plot_grid() 
    292     import matplotlib.pyplot as plt; plt.show() 
Note: See TracChangeset for help on using the changeset viewer.