0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Intel Fortran から Proj4 の機能を使う

Last updated at Posted at 2025-04-21

1.はじめに

 この記事では、Ubuntuで Intel Fortran から Proj4 の機能を使う方法をご紹介します。
 ほんとは iso_c_binding など使って gfortran でも使えるサンプルを用意したかったんですが、勉強不足なため使い慣れた Intel Fortran のディレクティブで実装しました。

2.Proj4 のインストール

2.1.Projとは

 Proj はGISソフトでも使用されている投影変換のライブラリです。proj-stringと呼ばれる文字列で投影方法を指定して、投影変換を行ってくれます。
Proj のサイト
 Proj は2025年4月現在、最新版はver9.6ですが、ver5からそれ以前とAPIが大きく変わりました。この記事では、使い方が簡単なver4以前のAPIを使用する方法をご紹介します。

2.2.Proj のパッケージ

 Ubuntuでは、libproj-dev という Proj の開発用パッケージが用意されていて、既にインストールされている環境が多いと思いますが、ver8以降ではver4のAPIが使えなくなっているため、ver4を別途インストールします。

2.3.oneAPI について

 事前にoneAPIをインストールし、icxまたはiccが実行できるようにしておいてください。Qiitaで「oneAPI ubuntu」で検索するといろいろ出てきます。

2.4.ソースのダウンロード

 ver4の最終版は2025年4月現在、ver4.9.3なので
Proj のダウンロードページ
から、proj-4.9.3.tar.gz をダウンロードしてきてください。

2.5.ビルドしてインストール

 ダウンロードした proj-4.9.3.tar.gz を展開可能な場所に置いて

tar xvzf proj-4.9.3.tar.gz
cd proj-4.9.3
./configure --prefix=$HOME/usr CC=icx
make
make install

とすると、$HOME/usrの下にインストールされます。icx は icc でも大丈夫です。
インストール先はどこでも良いですが、この記事では$HOME/usrにインストールした前提で説明します。

3.Intel Fortran からの呼び出し

 インターフェース定義モジュール

proj4.f90
    module proj4

    implicit none
    
    private
    public      ::  pjf90_init_plus, pjf90_transform, pjf90_free, pjf90_is_latlong
    
    interface

    integer(8) function pj_init_plus(cprojstring)
!dec$ attributes c,reference,alias:'pj_init_plus' :: pj_init_plus
        character(len=*), intent(in)    ::  cprojstring
!dec$ attributes reference              ::  cprojstring
    end function pj_init_plus
    
    integer(4) function pj_transform(src, dst, point_count, point_offset, x, y, z)
!dec$ attributes c,reference,alias:'pj_transform' :: pj_transform
        integer(8), intent(in)          ::  src, dst, z
        integer(4), intent(in)          ::  point_count, point_offset
        real(8), intent(in)             ::  x, y
!dec$ attributes value                  ::  src, dst, point_count, point_offset, z
!dec$ attributes reference              ::  x, y
    end function pj_transform

    subroutine pjf90_free(prj)
!dec$ attributes c,reference,alias:'pj_free' :: pjf90_free
        integer(8), intent(in)          ::  prj
!dec$ attributes value                  ::  prj
    end subroutine pjf90_free

    
    integer(4) function pjf90_is_latlong(prj)
!dec$ attributes c,reference,alias:'pj_is_latlong' :: pjf90_is_latlong
        integer(8), intent(in)          ::  prj
!dec$ attributes value                  ::  prj
    end function pjf90_is_latlong
    
    end interface

    interface pjf90_transform
        module procedure pjf90_transformN, pjf90_transform1
    end interface pjf90_transform

    contains
    
    integer(8) function pjf90_init_plus(cprojstring)
        implicit none
        character(len=*), intent(in)    ::  cprojstring
        pjf90_init_plus = pj_init_plus(trim(cprojstring)//char(0))
    end function pjf90_init_plus

    integer(4) function pjf90_transformN(src, dst, np, x, y)
        implicit none
        integer(8), intent(in)          ::  src, dst
        integer(4), intent(in)          ::  np
        real(8), intent(inout)          ::  x(*), y(*)
        integer(4)          ::  ip
        real(8), parameter  ::  pi = 3.14159265358979323846d0
        if (pjf90_is_latlong(src) /= 0) then
            do ip=1, np
                x(ip) = x(ip) / 180d0 * pi
                y(ip) = y(ip) / 180d0 * pi
            end do
        end if
        pjf90_transformN = pj_transform(src, dst, np, 1, x(1), y(1), 0)
        if (pjf90_is_latlong(dst) /= 0) then
            do ip=1, np
                x(ip) = x(ip) * 180d0 / pi
                y(ip) = y(ip) * 180d0 / pi
            end do
        end if
    end function pjf90_transformN

    integer(4) function pjf90_transform1(src, dst, x, y)
        implicit none
        integer(8), intent(in)          ::  src, dst
        real(8), intent(inout)          ::  x, y
        real(8)     ::  xx(1), yy(1)
        xx(1) = x
        yy(1) = y
        pjf90_transform1 = pjf90_transformN(src, dst, 1, xx, yy)
        x = xx(1)
        y = yy(1)
    end function pjf90_transform1

    end module proj4

 オリジナルのpj_transformでは、Z座標が求められたり、地点データ間のオフセット量を指定できますが、それは使わないので除外しました。
 また、オリジナルのpj_transformでは、緯度・経度座標はラジアン値で渡す必要があるのですが、呼び出し元ではdegree値で管理したいので、pj_transformの呼び出しの前後でラジアン値とdegree値を変換すようにしました。

 テストプログラム

t_proj4.f90
    program t_proj4
    
    use proj4

    implicit none

    integer(8)              ::  src, dst
    integer(4)              ::  ierr, ip
    integer(4), parameter   ::  np = 38
    real(8)                 ::  lons(np) = (/ 143.2586940d0, 143.2587500d0, 143.2587500d0, 141.6699440d0, 141.6699440d0, 141.0033330d0, 141.0033330d0, 140.4766940d0, 140.4766940d0, 140.2912780d0, 140.2912780d0, 141.1870830d0, 141.1870830d0, 139.9837220d0, 139.9837220d0, 139.8237220d0, 139.8237220d0, 140.5228890d0, 140.5228890d0, 140.3433610d0, 140.3433610d0, 141.4170280d0, 141.4170280d0, 141.9133060d0, 141.9133060d0, 143.7349440d0, 143.7349440d0, 144.7758060d0, 144.7758060d0, 145.3433330d0, 145.3433330d0, 145.2295280d0, 145.2295280d0, 145.8178610d0, 145.8178610d0, 143.9940830d0, 143.9940830d0, 143.2586940d0 /)
    real(8)                 ::  lats(np) = (/ 41.9991110d0, 41.9983060d0, 41.9983060d0, 42.6495280d0, 42.6495280d0, 42.3012220d0, 42.3012220d0, 42.5887220d0, 42.5887220d0, 42.2491670d0, 42.2491670d0, 41.8008060d0, 41.8008060d0, 41.5558060d0, 41.5558060d0, 42.6141670d0, 42.6141670d0, 42.9908610d0, 42.9908610d0, 43.3353890d0, 43.3353890d0, 43.3224720d0, 43.3224720d0, 45.5187220d0, 45.5187220d0, 44.1062500d0, 44.1062500d0, 43.9270830d0, 43.9270830d0, 44.3420560d0, 44.3420560d0, 43.3366670d0, 43.3366670d0, 43.3866390d0, 43.3866390d0, 42.9270560d0, 42.9270560d0, 41.9991110d0 /)
    real(8)                 ::  xx(np), yy(np)

    src = pjf90_init_plus('+proj=longlat +datum=WGS84')
    dst = pjf90_init_plus('+proj=tmerc +lat_0=44 +lon_0=142.25 +k=0.9999 +ellps=GRS80')
    
    xx = lons
    yy = lats

    ierr = pjf90_transform(src, dst, np, xx, yy)

    do ip=1, np
        write(*, '(2f20.2)') xx(ip), yy(ip)
    end do
    
    call pjf90_free(src)
    call pjf90_free(dst)

    stop
    
    contains

    end program t_proj4

 コンパイルはこの2つのファイルがあるフォルダで

ifx proj4.f90 t_proj4.f90 ${HOME}/usr/lib/libproj.a -o t_proj4.exe

とします。ifxの代わりにifortでも大丈夫です。
 この例では、北海道の輪郭のWGS84の緯度・経度座標値を、平面直角座標12系(JGD2000)値に変換します

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?