วันอาทิตย์ที่ 3 สิงหาคม พ.ศ. 2557

ขั้นตอน-การทำการประมาณค่าน้ำฝนด้วยวิธี IDW


2014.08.03
ขั้นตอนการทำการประมาณค่าน้ำฝนด้วยวิธี IDW จากข้อมูลตัวอย่าง

การประมาณค่าข้อมูลเชิงพื้นที่ใดๆที่ได้มาจากภาคสนามนั้นเป็นขั้นตอนสำคัญต่อการวิเคราะห์ข้อมูลเชิงพื้นที่ จากข้อมูลที่กระจัดกระจายในพื้นที่ นำมาผ่านวิธีการประมาณค่าให้เป็นข้อมูลเชิงพื้นผิวหรือข้อมูลเชิงกริดเพื่อให้ง่ายต่อการวิเคราะห์ วิธีการที่นำมาใช้ในการประมาณมีตั้งแต่ง่ายไปจนถึงซับซ้อน ไม่ว่าจะเป็น linear interpolation, Thiessen, Fixed-radius, Natural neighbor, Trend surface interpolation,  IDW, Spline, Kriging เป็นต้น ซึ่งในแต่ละประเภทก็จะมีแยกย่อยไปเยอะแยะ แล้วแต่ความต้องการในงานนั้นๆ

ในส่วนของผมนั้น ผมต้องการทำการประมาณค่าน้ำฝนรายชั่วโมงหลายช่วงฤดูกาล หากใช้โปรแกรมช่วยพวก GIS, R,  Python หรือโปรแกรมเชิงพื้นที่อื่นๆก็อาจจะต้องใช้เทคนิคเข้าช่วยเพราะข้อมูลเยอะ ในที่นี้ผมจึงอยากทดลองเขียนโปรแกรมด้วยฟอร์แทรนเพื่อการประมวลผลข้อมูลน้ำฝนตัวอย่าง หากมีข้อมูลเยอะสามารถใช้ bash script เข้าช่วยในการประมวลผลแล้วเพียงนั่งรอทำงานอื่นไป โปรแกรมที่นำมาแสดงนี้ใช้วิธีการ IDW โดยสามารถกำหนดระยะรัศมีของการประมาณค่า และค่ายกกำลังของส่วนกลับระยะทาง ดูภาพประกอบนะครับ โปรแกรมจะคำนวนค่าน้ำหนักตามส่วนกลับของระยะทางในรัศมีที่ได้ระบุไว้ ลองเอาไปเล่นดูนะครับ หากมีข้อบกพร่องประการใดแจ้งให้ทราบได้เลยนะครับ จะได้ปรับปรุงต่อไป


1.       โปรแกรมฟอร์แทรน IDW (Inverse Distance Weighted Interpolation)
program simple_weighted_interpolation
!------------------------------------------------------------------------
! 2014.08.03 NATTAPON MAHAVIK
! interpolation of scattered data (i.e.rain) to fine grid
! suppose we have set of rain at stations that are located over observation area
! we want to interpolate it simply to fine grid.
! here we use inverse weighted distance of
! those scatteredd points to  fine grid at prefered radius distance
! concept of interpolation IDW:
!------------------------------------------------------------------------
implicit none
real,parameter :: lat1=10.0,lon1=100.0,lat2=40.0,lon2=140.0   !-geographic coordinate of boundary interpolated data
real,parameter :: res=1.0              !-grid resolution in degree
real,parameter :: radi=20.0            !-prefered radius of interpolation degree
integer,parameter :: nx=nint((lon2-lon1+res)/res),ny=nint((lat2-lat1+res)/res)    !-number of grid in x,y direction
real*4,dimension(nx,ny) :: outp_int    !-array of result here is rainfall rate

!-rain sample data
integer,parameter :: nums=15           !-number of rain stations
real,dimension(nums),parameter:: rain=(/10.0,5.0,1.0,20.0,30.0,10.0,5.0,1.0,20.0,30.0,10.0,5.0,1.0,20.0,30.0/)  !-rain rate [mm]
real,dimension(nums),parameter:: lonr=(/105.5,110.0,115.0,127.0,135.0,105.5,110.0,115.0,127.0,135.0,105.5,110.0,115.0,127.0,135.0/) !-longitude of rain stations [degree]
real,dimension(nums),parameter:: latr=(/15.0,13.0,17.0,19.0,14.0,25.0,23.0,27.0,29.0,24.0,35.0,33.0,37.0,39.0,34.0/) !-latitude of rain stations
integer,parameter :: sizeofreal=4

print*,'number of column and row: ',nx,ny
print*,'resolution: ',res,' degree'
!--call interpolation function IDW
call intp_by_dist(lat1,lat2,lon1,lon2,res,nx,ny,radi,nums,rain,latr,lonr,outp_int)

!--write interpolated rain to binary for checking in grads
open (10,file='rain_int_sample.bin',access='direct', recl=nx*ny*sizeofreal)
write(10,rec=1)outp_int
close(10)
endprogram simple_weighted_interpolation

subroutine intp_by_dist(lat1,lat2,lon1,lon2,res,nx,ny,radi,nums,rain,latr,lonr,outp_int)
!-------------------------------------------------------
! calculate weigth by distance and apply to varaibles

implicit none
real,intent(in) :: lat1,lon1,lat2,lon2,res,radi
integer,intent(in) :: nx,ny,nums
real,dimension(nums),intent(in) :: rain
real,dimension(nums),intent(in) :: latr
real,dimension(nums),intent(in) :: lonr
real,dimension(nx,ny),intent(out):: outp_int
real,dimension(nx,ny):: sum_inv_dist
real,dimension(nx,ny):: sum_weight_rain
integer :: sx,ex,sy,ey  ! start and end grid within defined radius of interpolation
real :: lon,lat         ! geo coordinate of considering grid
integer :: i,j,m,n                                                                                                 
real :: dist !-distance between considering rain sta. and fine grid in [m]
real,parameter :: p=-2           !- power of inverse distance
!-Function calculating distance on sphere
real :: haversine      !-Function name for calculating distance on sphere

print*,'interpolaing by inverse distance weighted...'

!-loop each rain station
do i=1,nums
   sx=nint(((lonr(i)-radi)-lon1)/res)
   ex=nint(((lonr(i)+radi)-lon1)/res)
   sy=nint(((latr(i)-radi)-lat1)/res)
   ey=nint(((latr(i)+radi)-lat1)/res)
     
   !-loop within defined radius in output grid
   do m=sx,ex
      do n=sy,ey
         !-geocoordinate of considering grid
         lon=lon1+(m*res)
         lat=lat1+(n*res)
        

       !-to avoid out of boundary
         if ((m<1 m="" n="" or.="">nx).or.(n>ny)) cycle

         !-cal distance of considering rain and output grid
         dist=haversine(lat,lon,latr(i),lonr(i))
         if (dist==0.0) then
            sum_inv_dist(m,n)=1.0
            sum_weight_rain(m,n)=rain(i)
            cycle
         else
            sum_inv_dist(m,n)=sum_inv_dist(m,n)+(dist**p)
            sum_weight_rain(m,n)=sum_weight_rain(m,n)+((dist**p)*rain(i))
         end if
      end do
   end do
end do

!-evaluate interpolated rain by IDW
outp_int=sum_weight_rain/sum_inv_dist        
end subroutine intp_by_dist

 
function haversine(dlat1,dlon1,dlat2,dlon2)
    implicit none
    real,intent(in) :: dlat1,dlon1,dlat2,dlon2                     !- lat lon in degree converted to radian
    real :: haversine         !- distance will be return by this var [m]
    real :: lat1,lat2
    real :: a,c
    real, parameter :: pi = 4*atan(1.0)   !- exploit intrinsic atan to generate pi
    real,parameter :: d2r=pi/180.0 !- radian conversion degree to radian
    real :: dlat,dlon            !- difference in lat and lon in radian
    real,parameter :: re=6372.8*1000.0    !- earth radius in m

    dlat=(dlat2-dlat1)*d2r
    dlon=(dlon2-dlon1)*d2r
    lat1=dlat1*d2r
    lat2=dlat2*d2r
   
    !-Haversine formula
    a=(sin(dlat/2))**2+cos(lat1)*cos(lat2)*(sin(dlon/2)**2)
    c=2*atan2(sqrt(a),sqrt(1-a))
    haversine=re*c
end function haversine

สร้างคอนโทรลไฟล์ของโปรแกรม GRADS ตามข้างล่างแล้วเปิดตรวจดูใน GRADS
dset ^rain_int_sample.bin
title Interpolated sample wind map 1.0 deg
undef -9999.0
XDEF 41  LINEAR   100   1.000
YDEF 31  LINEAR   10    1.000
zdef 1 levels 0 1
tdef 1 linear 22Z24aug2010 1hr
vars 1
r     0 99 rainfall rate interpolated [mm]
endvars
 
ผลลัพธ์ที่ได้ตามภาพข้างล่างเป็นไปตามที่คาดไว้ ถือว่าโปรแกรมน่าจะไม่ผิดพลาดอะไรนะครับ หากต่อไปคงต้องพิจารณาจำนวนจุดที่ใช้ในการประมาณค่าแทนรัศมี หรือทดลองการประมาณค่าแบบที่ซับซ้อนมากขึ้น

ไม่มีความคิดเห็น:

แสดงความคิดเห็น