内閣府 中央防災会議が使った謎の緯度経度を基にしたUTM座標系を変換する方法

#目的
JAGURSという津波シミュレーションを行うためのデータ作り。
内閣府中央防災会議が謎の緯度経度座標系を使ってくれて扱いにすごく悩んだのでここに記します。
誰かの役に立てばいいね！

そして、僕が約15年前に作った座標変換プログラムのメモ帳です。
（正確には、今はなき緯度経度変換ページのJavaScriptをfortran化させたものです）


データの入手等はぼくが前に書いた[記事](https://qiita.com/iowanman/items/55574fea8d62d1270a60)を見てください。

#UTM座標系
UTMとは、wikipedia先生の [ユニバーサル横メルカトル図法](https://ja.wikipedia.org/wiki/%E3%83%A6%E3%83%8B%E3%83%90%E3%83%BC%E3%82%B5%E3%83%AB%E6%A8%AA%E3%83%A1%E3%83%AB%E3%82%AB%E3%83%88%E3%83%AB%E5%9B%B3%E6%B3%95)を見てください。

ひらたく言うと、地球は丸いけど、二次元平面に投影しますよ、というやつです。
日本近海の津波のシミュレーションをやる場合は直交座標系で十分です（たぶん

#日本の緯度経度座標系
旧測地系と呼ばれるものと、世界測地系と呼ばれるものが混在してるので気をつけましょう。
特に日本では旧測地系と呼ばれるものが、未だに大活躍しています。

#中央防災会議が使った謎の測地系
ちなみにG空間情報センターからダウンロードできる津波計算用のデータで、南海トラフはUTM53なので一般的に公開されているソフトやライブラリで対応できますが、東北用が謎です。
公式ドキュメントのスクショを以下に示します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180426/9ea987e5-b42a-6e60-53a8-2edfdb7e323e.png)

僕には「データの作成にあたっては、中心軽度を１４３度とするUTM座標系を投影法として使用した。東西方向の原点の値は500,000mとした。」という日本語が僕にとってはまじで意味不明でした。


#緯度経度 UTM 相互変換プログラム
ちなみに測地系の違いについて、僕は全く理解していません。
このプログラムの正しさについては、いろんな変換サイトで同じ結果が出ることを確認しただけですので、動作を保証するものではありません。
現代では、これを実現するライブラリがPythonなどでは公開されているのでそちらを利用することを強くお勧めします。

使い方 wgs2utm53(y,x,id,kd)　y:南北 x:東西 id:緯度 kd:軽度 (なぜかUTMの世界ではxは南北方向らしいので注意)



```fortran
        !**************************************
        subroutine tokyo2wgs(bw,lw,bt,lt)
        implicit none
        real(kind=8) bt,lt
        real(kind=8) bw,lw
        real(kind=8) ht,hw
        real(kind=8) aw,fw,e2w
        real(kind=8) at,ft,e2t
        
        real(kind=8) dx,dy,dz
        real(kind=8) x,y,z

        ht =0.d0
        aw = 6378137.d0
        fw = 1 / 298.257223d0
        e2w = 2d0*fw - fw*fw
        at = 6377397.155d0
        ft = 1.d0 / 299.152813d0
        e2t = 2.d0*ft - ft*ft
        dx = -148.d0
        dy = 507.d0
        dz = 681.d0

        call llh2xyz(x,y,z,bt,lt,ht,at,e2t)
        call xyz2llh(bw,lw,hw,x+dx,y+dy,z+dz,aw,e2w)
        return
       end 
       !***********************************************


       !***********************************************
       subroutine tokyo2utm(x,y,b,l)
       implicit none
       real(kind=8) b,l
       real(kind=8) x,y 
       
       real(kind=8) rd
       integer zn
       real(kind=8) l0
       real(kind=8) a,f,e2
       
       rd =3.14159265358979323846264338327950288419716939937d0/180.d0
       a = 6377397.155d0
       f = 1d0 / 299.152813d0

       e2=2.0d0*f-f*f
       zn = mod(int((l+180.d0)/6.d0),60)+1
       l0=dble(zn)*6.d0-183.d0 !原点だそうだ。
       !write(*,*) '中央罫線：東経',l0
       call ll2xy(x,y,b*rd,(l-l0)*rd,a,f,e2)
       x = x*0.9996d0
       y = y*0.9996d0 
       y = y+500000.d0
       return
       end 
       !***********************************************

       !***********************************************
       subroutine wgs2utm(x,y,b,l)
       implicit none
       real(kind=8) b,l
       real(kind=8) x,y 
       
       real(kind=8) rd
       integer zn
       real(kind=8) l0
       real(kind=8) a,f,e2
       
       rd =3.14159265358979323846264338327950288419716939937d0/180.d0
       a=6378137.d0
       f=1.0d0/298.257223d0
       e2=2.0d0*f-f*f
       zn = mod(int((l+180.d0)/6.d0),60)+1
       l0=dble(zn)*6.d0-183.d0 !原点だそうだ。
       !write(*,*) '中央罫線：東経',l0
       call ll2xy(x,y,b*rd,(l-l0)*rd,a,f,e2)
       x = x*0.9996d0
       y = y*0.9996d0 
       y = y+500000.d0
       return
       end
       !***********************************************

       !***********************************************
       subroutine wgs2utm53(x,y,b,l)
       implicit none
       real(kind=8) b,l
       real(kind=8) x,y 
       
       real(kind=8) rd
       integer zn
       real(kind=8) l0
       real(kind=8) a,f,e2
       
       rd =3.14159265358979323846264338327950288419716939937d0/180.d0
       a=6378137.d0
       f=1.0d0/298.257223d0
       e2=2.0d0*f-f*f
       zn = 53  !mod(int((l+180.d0)/6.d0),60)+1
       l0=dble(zn)*6.d0-183.d0 !原点だそうだ。
       !write(*,*) '中央罫線：東経',l0

       call ll2xy(x,y,b*rd,(l-l0)*rd,a,f,e2)
       x = x*0.9996d0
       y = y*0.9996d0 
       y = y+500000.d0
       return
       end
       !***********************************************

       !**************************************************
       subroutine utm2wgs(b,l,x,y,zn)
        implicit none
        real(kind=8),intent(out):: b,l
        real(kind=8),intent(in)::x,y
        integer,intent(in) :: zn
        real(kind=8) :: a,f,e2,ed2,e4,e6,e8
        real(kind=8) :: cad,cbd,ccd,cdd,ced
        real(kind=8) :: l0,eps,m0,cb,eta2,rn
        real(kind=8) :: xd,yd,tx,ty
        real(kind=8) :: db,dl

        real(kind=8) :: rd
        rd =3.14159265358979323846264338327950288419716939937d0/180.d0

        a   = 6378137.d0!        # 赤道半径 (WGS 84)
        f   = 1.d0 / 298.257223d0 ! # 扁平率 (WGS 84)
        e2  = 2.d0*f - f*f !;   # 第1離心率
        ed2 = e2 / (1.d0-e2)!;  # 第2離心率
        e4 = e2 * e2
        e6 = e4 * e2
        e8 = e6 * e2
       cad=a*(1.d0-e2)*(1.d0+3.d0/4.d0*e2+45.d0/64.d0*e4
     & +175.d0/256.d0*e6+11025.d0/16384.d0*e8)
       cbd=a*(1.d0-e2)*(  3.d0/4.d0*e2+15.d0/16.d0*e4
     & +525.d0/512.d0*e6+  2205.d0/2048.d0*e8)
       ccd=a*(1.d0-e2)*(         15.d0/64.d0*e4
     & +105.d0/256.d0*e6+  2205.d0/4096.d0*e8)
       cdd=a*(1.d0-e2)*(                   
     & 35.d0/512.d0*e6+   315.d0/2048.d0*e8)
       ced=a*(1.d0-e2)*(                                
     & 315.d0/16384.d0*e8)
        l0 = dble(zn)*6.d0-183.d0
        eps = 0.0000001d0/3600.d0*rd
        m0 = 0.9996d0
        ty = y-500000.d0
        tx = x/m0
        ty = ty/m0
        db = 1.d0
        dl = 1.d0
        b = tx/cad
        cb = dcos(b)
        eta2 = ed2*cb*cb
        rn = a/(1.d0-f)/dsqrt(1.d0+eta2)
        l=ty/rn/cb
        do while(db>eps .or. dl>eps)
         call ll2xy(xd,yd,b,l,a,f,e2)
         db = (tx-xd)/cad
         cb = dcos(b)
         eta2=ed2*cb*cb
         rn = a/(1.d0-f)/dsqrt(1.d0+eta2)
         dl = (ty-yd)/rn/cb
         b = b+db
         l = l+dl
        end do
        b = b/rd
        l = l/rd+l0
       end subroutine utm2wgs
       !****************************************************

       !**************************************************
       subroutine utm2tokyo(b,l,x,y,zn)
        implicit none
        real(kind=8),intent(out):: b,l
        real(kind=8),intent(in)::x,y
        integer,intent(in) :: zn
        real(kind=8) :: a,f,e2,ed2,e4,e6,e8
        real(kind=8) :: cad,cbd,ccd,cdd,ced
        real(kind=8) :: l0,eps,m0,cb,eta2,rn
        real(kind=8) :: xd,yd,tx,ty
        real(kind=8) :: db,dl

        real(kind=8) :: rd
        rd =3.14159265358979323846264338327950288419716939937d0/180.d0

        !a   = 6378137.d0!        # 赤道半径 (WGS 84)
        !f   = 1.d0 / 298.257223d0 ! # 扁平率 (WGS 84)
        a   = 6377397.155d0        !;    # 赤道半径 (Tokyo)
        f   = 1.d0 / 299.152813d0    !; # 扁平率 (Tokyo)
        
        e2  = 2.d0*f - f*f !;   # 第1離心率
        ed2 = e2 / (1.d0-e2)!;  # 第2離心率
        e4 = e2 * e2
        e6 = e4 * e2
        e8 = e6 * e2
       cad=a*(1.d0-e2)*(1.d0+3.d0/4.d0*e2+45.d0/64.d0*e4
     & +175.d0/256.d0*e6+11025.d0/16384.d0*e8)
       cbd=a*(1.d0-e2)*(  3.d0/4.d0*e2+15.d0/16.d0*e4
     & +525.d0/512.d0*e6+  2205.d0/2048.d0*e8)
       ccd=a*(1.d0-e2)*(         15.d0/64.d0*e4
     & +105.d0/256.d0*e6+  2205.d0/4096.d0*e8)
       cdd=a*(1.d0-e2)*(                   
     & 35.d0/512.d0*e6+   315.d0/2048.d0*e8)
       ced=a*(1.d0-e2)*(                                
     & 315.d0/16384.d0*e8)
        l0 = dble(zn)*6.d0-183.d0
        eps = 0.0001d0/3600.d0*rd
        m0 = 0.9996d0
        ty = y-500000.d0
        tx = x/m0
        ty = ty/m0
        db = 1.d0
        dl = 1.d0
        b = tx/cad
        cb = dcos(b)
        eta2 = ed2*cb*cb
        rn = a/(1.d0-f)/dsqrt(1.d0+eta2)
        l=ty/rn/cb
        do while(db>eps .or. dl>eps)
         call ll2xy(xd,yd,b,l,a,f,e2)
         db = (tx-xd)/cad
         cb = dcos(b)
         eta2=ed2*cb*cb
         rn = a/(1.d0-f)/dsqrt(1.d0+eta2)
         dl = (ty-yd)/rn/cb
         b = b+db
         l = l+dl
        end do
        b = b/rd
        l = l/rd+l0
       end subroutine utm2tokyo
       !****************************************************

       !***********************************************
       subroutine ll2xy(x,y,b,dl,a,f,e2)
       implicit none
       real(kind=8) x,y
       real(kind=8) b,dl
       real(kind=8) a,f,e2
       real(kind=8) dl2,dl4
       real(kind=8) sb,cb,cb3,cb5,tb2,tb4,ed2,eta2,rn
       real(kind=8) lom
       
       x=0.d0
       y=0.d0
       dl2=dl**2
       dl4=dl**4
       sb=dsin(b)
       cb=dcos(b)
       cb3 = cb*cb*cb
       cb5 = cb3*cb*cb
       tb2 = sb*sb/cb/cb
       tb4 = tb2*tb2
       ed2=e2/(1.d0-e2)
       eta2 = ed2*cb*cb
       rn=a/(1.d0-f)/dsqrt(1.d0+eta2)
       x=lom(b,a,f,e2)
       !write(*,*) '赤道からの距離:',nint(x)/1000.d0
       x =x+ rn/2.d0*sb*cb*dl2
       x =x+ rn/24.d0*sb*cb3*(5.d0-tb2+9.d0*eta2+4.d0*eta2*eta2)*dl4
       x =x+ rn/720.d0*sb*cb5*(61.d0-58.d0*tb2+tb4)*dl4*dl2
       y = rn*cb*dl
       y =y+ rn/6.d0*cb3*(1.d0-tb2+eta2)*dl*dl2
       y =y+ rn/120.d0*cb5*(5.d0-18.d0*tb2+tb4)*dl*dl4
       return
       end
       !*****************************************************

       !*****************************************************
       function lom(b,a,f,e2)
       implicit none
       real(kind=8) lom
       real(kind=8) b,a,f,e2
       real(kind=8) e4,e6,e8
       real(kind=8) cad,cbd,ccd,cdd,ced
       e4=e2*e2
       e6=e4*e2
       e8=e6*e2
       cad=a*(1.d0-e2)*(1.d0+3.d0/4.d0*e2+45.d0/64.d0*e4
     & +175.d0/256.d0*e6+11025.d0/16384.d0*e8)
       cbd=a*(1.d0-e2)*(  3.d0/4.d0*e2+15.d0/16.d0*e4
     & +525.d0/512.d0*e6+  2205.d0/2048.d0*e8)
       ccd=a*(1.d0-e2)*(         15.d0/64.d0*e4
     & +105.d0/256.d0*e6+  2205.d0/4096.d0*e8)
       cdd=a*(1.d0-e2)*(                   
     & 35.d0/512.d0*e6+   315.d0/2048.d0*e8)
       ced=a*(1.d0-e2)*(                                
     & 315.d0/16384.d0*e8)
       lom= cad*b-cbd/2.d0*dsin(2.d0*b)+ccd/4.d0*dsin(4.d0*b)
     & -cdd/6.d0*dsin(6.d0*b)+ced/8.d0*dsin(8.d0*b)
       return      
       end
       !***************************************************

       !*****************************************************
       subroutine llh2xyz(x,y,z,b,l,h,a,e2) 
        implicit none
        real(kind=8) x,y,z
        real(kind=8) b,l,h,a,e2
        real(kind=8) br,lr,sb,cb,rn
        real(kind=8) rd 
       rd =3.14159265358979323846264338327950288419716939937d0/180.d0
       
       br = b*rd
       lr = l*rd
       sb = dsin(br)
       cb = dcos(br)

       rn = a/dsqrt(1.d0-e2*sb*sb)
       x = (rn+h) * cb * dcos(lr)
       y = (rn+h) * cb * dsin(lr)
       z = (rn*(1.d0-e2)+h) * sb
       !write(*,*)e2 
       end 
       !***************************************************

       !*************************************************
        subroutine xyz2llh(b,l,h,x,y,z,a,e2)
        implicit none
        real(kind=8) x,y,z,a,e2
        real(kind=8) b,l,h
        real(kind=8) bda,p,t,st,ct,sb,rn
        real(kind=8) rd 
       rd =3.14159265358979323846264338327950288419716939937d0/180.d0
        
        bda = dsqrt(1.d0-e2)
        p = dsqrt(x**2+y**2)
        t = datan2(z, p*bda)
        st = dsin(t)
        ct = dcos(t)
        b = datan2(z+e2*a/bda*st**3, p-e2*a*ct**3)
        l = datan2(y, x)
        sb = dsin(b)
        rn = a / dsqrt(1.d0-e2*sb*sb)
        h = p/dcos(b) - rn
       
        b = b/rd
        l = l/rd
        end 
        !******************************************************
```

#なぞの座標系への対応

「データの作成にあたっては、中心軽度を143度とするUTM座標系を投影法として使用した。東西方向の原点の値は500,000mとした。」
とのころなので、プログラム中のl0を143にして~~出てきた東西方向の値から500000引いたら~~いいんやろなぁと思いました。

ので以下のサブルーチンを作りました。

```fortran
       !**************************************************
       subroutine utm2wgs_nazo(b,l,x,y,zn)
        implicit none
        real(kind=8),intent(out):: b,l
        real(kind=8),intent(in)::x,y
        integer,intent(in) :: zn
        real(kind=8) :: a,f,e2,ed2,e4,e6,e8
        real(kind=8) :: cad,cbd,ccd,cdd,ced
        real(kind=8) :: l0,eps,m0,cb,eta2,rn
        real(kind=8) :: xd,yd,tx,ty
        real(kind=8) :: db,dl

        real(kind=8) :: rd
        rd =3.14159265358979323846264338327950288419716939937d0/180.d0

        a   = 6378137.d0!        # 赤道半径 (WGS 84)
        f   = 1.d0 / 298.257223d0 ! # 扁平率 (WGS 84)
        e2  = 2.d0*f - f*f !;   # 第1離心率
        ed2 = e2 / (1.d0-e2)!;  # 第2離心率
        e4 = e2 * e2
        e6 = e4 * e2
        e8 = e6 * e2
       cad=a*(1.d0-e2)*(1.d0+3.d0/4.d0*e2+45.d0/64.d0*e4
     & +175.d0/256.d0*e6+11025.d0/16384.d0*e8)
       cbd=a*(1.d0-e2)*(  3.d0/4.d0*e2+15.d0/16.d0*e4
     & +525.d0/512.d0*e6+  2205.d0/2048.d0*e8)
       ccd=a*(1.d0-e2)*(         15.d0/64.d0*e4
     & +105.d0/256.d0*e6+  2205.d0/4096.d0*e8)
       cdd=a*(1.d0-e2)*(                   
     & 35.d0/512.d0*e6+   315.d0/2048.d0*e8)
       ced=a*(1.d0-e2)*(                                
     & 315.d0/16384.d0*e8)
        l0 =143.d0   !dble(zn)*6.d0-183.d0
        eps = 0.0000001d0/3600.d0*rd
        m0 = 0.9996d0
        ty = y -500000.d0
        tx = x/m0
        ty = ty/m0
        db = 1.d0
        dl = 1.d0
        b = tx/cad
        cb = dcos(b)
        eta2 = ed2*cb*cb
        rn = a/(1.d0-f)/dsqrt(1.d0+eta2)
        l=ty/rn/cb
        do while(db>eps .or. dl>eps)
         call ll2xy(xd,yd,b,l,a,f,e2)
         db = (tx-xd)/cad
         cb = dcos(b)
         eta2=ed2*cb*cb
         rn = a/(1.d0-f)/dsqrt(1.d0+eta2)
         dl = (ty-yd)/rn/cb
         b = b+db
         l = l+dl
        end do
        b = b/rd
        l = l/rd+l0
       end subroutine utm2wgs_nazo
       !****************************************************
```
作ったプログラムがどれくらい正確か、海岸線を描いて見たいと思います。

```bash
#!/bin/bash

region=131/161/31/47
gmt makecpt -T-1000/4000/200 -Crainbow > color1.cpt
gmt xyz2grd depth_1350-01_idkd.dat -Gdepth_1350-01_idkd.grd -V -R${region} -I0.015
gmt grdimage depth_1350-01_idkd.grd -R${region} -JM20 -P -Bag -K -V > cccc.ps
gmt pscoast -R${region} -JM20 -Df -W1/0/0/0  -O -V >> cccc.ps

```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/180426/6afcb1ed-12d8-5c1f-9ec2-8c11eed8033d.png)

だいたいいい感じで他データから持ってきた海岸線データと一致してるのでたぶんOK
というわけで、僕が太古の昔に作ったプログラムの l0=143.d0 にしたらいいみたいです。もしくは、他のプログラムでUTMのZONEを指定できるなら、53.33333333でもいけるっぽいです。
（普通は整数以外を入れると計算できないけれども）

直交座標とメルカトルってやっぱこんなに違うのね。（この画像に至るまで自分が書いたプログラムのバグを含めて半日かかったorz)

変換プログラムによると、このデータの左上は（ 136.30697 47.86043）なのだそうです。


#まとめ
なんでこんなマニアックなことを書いたかと言うと、津波のシミュレーションのために、いろんな方が提案している断層モデルを入力する必要があるんですが、それらのモデルは緯度経度（旧測地だったりWGSだったり）するので、謎の緯度経度系を理解してないと変換できないんです！

だいたい、ドキュメントに左上の緯度経度が示されてないってどーゆーこと？

前にも書きましたが、南海トラフのデータはUTM53なので、ライブラリ等を利用することが可能です。