65.9K
CodeProject 正在变化。 阅读更多。
Home

Fortran 中点是否在 3D 凸多边形内

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2016 年 2 月 10 日

CPOL

2分钟阅读

viewsIcon

14149

downloadIcon

195

一个算法,用于确定点是否在给定多边形顶点 Fortran 的 3D 凸多边形内。

引言

这是原始 C++ 算法的 Fortran 版本,可以在这里找到。

背景

请参考原始 C++ 算法 这里

Using the Code

该算法被封装到一个 Fortran DLL GeoProc 中。主测试程序 LASProc 从 LAS 文件读取点云数据,然后将所有内部点写入一个新的 LAS 文件。LAS 文件可以使用免费软件 FugroViewer 显示。

要使用 GeoProc 模块库,首先准备一个 GeoPolygonProc 对象,像这样:

polygon = GeoPolygon(verticesArray)
call procObj%InitGeoPolygonProc(polygon);

要检查一个点是否在多边形内部:

procObj%PointInside3DPolygon(x, y, z)

以下是 DLL 库 GeoProc.dll 中的所有模块:

有关模块的解释和注释,请参考C++ 版本

GeoPoint.f03:

module GeoPoint_Module             
    implicit none
    
    ! data member
    type GeoPoint
        real :: x
        real :: y
        real :: z
    end type GeoPoint

    ! constructor
    interface GeoPoint
        module procedure new_GeoPoint
      end interface
 
    ! operator overloading
    interface operator (+)
        procedure add
    end interface operator (+)    

contains

    type(GeoPoint) function new_GeoPoint(x, y, z) result(pt)
        implicit none
        real, intent(in) :: x
        real, intent(in) :: y
        real, intent(in) :: z        
    
        pt%x = x
        pt%y = y
        pt%z = z        
      end function
      
    type(GeoPoint) function add(p1, p2) result(pt)
        implicit none
        type(GeoPoint), intent(in) :: p1
        type(GeoPoint), intent(in) :: p2          
          
        pt%x = p1%x + p2%x
        pt%y = p1%y + p2%y
        pt%z = p1%z + p2%z        
    end function add             
       
end module GeoPoint_Module

GeoVector.f03:

module GeoVector_Module       
    use GeoPoint_Module
    
    implicit none
    
    ! data member
    type GeoVector
        private
            type(GeoPoint) :: p0
            type(GeoPoint) :: p1
            real :: x
            real :: y
            real :: z        
    end type GeoVector             

    ! constructor
    interface GeoVector
        module procedure new_GeoVector
      end interface
 
    ! operator overloading
    interface operator (*)
        procedure multiple
    end interface operator (*)    
        
contains

    type(GeoVector) function new_GeoVector(p0, p1) result(vt)
        implicit none
        type(GeoPoint), intent(in) :: p0
        type(GeoPoint), intent(in) :: p1            
    
        vt%p0 = p0
        vt%p1 = p1
        vt%x = p1%x - p0%x
        vt%y = p1%y - p0%y
        vt%z = p1%z - p0%z         
      end function
      
    type(GeoVector) function multiple(v1, v2) result(vt)
        implicit none
        type(GeoVector), intent(in) :: v1
        type(GeoVector), intent(in) :: v2      
        
        vt%x = v1%y * v2%z - v1%z * v2%y
        vt%y = v1%z * v2%x - v1%x * v2%z
        vt%z = v1%x * v2%y - v1%y * v2%x
                            
        vt%p0 = v1%p0
        vt%p1 = vt%p0 + GeoPoint(vt%x, vt%y, vt%z);
                        
    end function multiple          

    real function get_x(vt) result(ret)
        implicit none
        type(GeoVector) :: vt        
        ret = vt%x
    end function get_x
    
    real function get_y(vt) result(ret)
        implicit none
        type(GeoVector) :: vt        
        ret = vt%y
    end function get_y
    
    real function get_z(vt) result(ret)
        implicit none
        type(GeoVector) :: vt        
        ret = vt%z
    end function get_z
       
end module GeoVector_Module

GeoPlane.f03:

module GeoPlane_Module       
    use GeoPoint_Module
    use GeoVector_Module
    
    implicit none
    
    ! Plane Equation: a * x + b * y + c * z + d = 0
    
    ! data member
    type GeoPlane
        real :: a
        real :: b
        real :: c
        real :: d
        contains
            procedure :: initGeoPlane                                    
    end type GeoPlane

    ! constructor
    interface GeoPlane
        module procedure new_GeoPlane
      end interface
 
    ! operator overloading
    interface operator (*)
        procedure multiplePoint
    end interface operator (*)    
    
    interface operator (-)
        module procedure negative
    end interface operator (-)

contains
    
    subroutine initGeoPlane(this, p0, p1, p2)
        implicit none
        class(GeoPlane) :: this
        type(GeoPoint) :: p0, p1, p2
        
        type(GeoVector) :: u, v, n

        v = GeoVector(p0, p1);

        u = GeoVector(p0, p2);

        n = u * v;

        ! normal vector        
        this%a = get_x(n);
        this%b = get_y(n);
        this%c = get_z(n);        

        this%d = -(this%a * p0%x + this%b * p0%y + this%c * p0%z);
    end subroutine initGeoPlane

    type(GeoPlane) function new_GeoPlane(a, b, c, d) result(pl)
        implicit none
        real, intent(in) :: a
        real, intent(in) :: b
        real, intent(in) :: c       
        real, intent(in) :: d
    
        pl%a = a
        pl%b = b
        pl%c = c
        pl%d = d        
      end function
      
    real function multiplePoint(pl, pt) result(ret)
        implicit none
        type(GeoPlane), intent(in) :: pl
        type(GeoPoint), intent(in) :: pt        
        
        ret = pt%x * pl%a + pt%y * pl%b + pt%z * pl%c + pl%d          
    end function multiplePoint   

    type(GeoPlane) function negative(this) result(pl)
        implicit none
        type(GeoPlane), intent(in) :: this
            
        pl%a = -this%a
        pl%b = -this%b
        pl%c = -this%c
        pl%d = -this%d        
      end function    
       
end module GeoPlane_Module

GeoPolygon.f03:

module GeoPolygon_Module       
    use GeoPoint_Module
    use Utility_Module
    
    implicit none
    
    ! data member
    type GeoPolygon
        type(GeoPoint), dimension(:), allocatable :: pts
        integer, dimension(:), allocatable :: idx
        integer :: n                                            
    end type GeoPolygon

    ! constructor
    interface GeoPolygon
        module procedure new_GeoPolygon
      end interface
      
contains
        
    type(GeoPolygon) function new_GeoPolygon(ptsIn) result(this)
        implicit none
        type(GeoPoint), dimension(:), intent(in) :: ptsIn
        integer :: i, isize        
        
        isize = size(ptsIn)
        
        this%n = isize
        
        allocate(this%idx(isize))
        allocate(this%pts(isize))
        
        do i = 1, isize                        
            this%pts(i) = ptsIn(i)
            this%idx(i) = i
        end do
        
      end function new_GeoPolygon
    
    subroutine destructor(this)
        implicit none
        type(GeoPolygon) :: this        
        if (allocated(this % pts)) deallocate(this % pts)
        if (allocated(this % idx)) deallocate(this % idx)
    end subroutine    
      
 
end module GeoPolygon_Module

GeoFace.f03:

module GeoFace_Module       
    use GeoPoint_Module
    use Utility_Module
    
    implicit none
    
    ! data member
    type GeoFace
        type(GeoPoint), dimension(:), allocatable :: pts
        integer, dimension(:), allocatable :: idx
        integer :: n                                            
    end type GeoFace

    ! constructor
    interface GeoFace
        module procedure new_GeoFace
      end interface GeoFace
      
contains
        
    type(GeoFace) function new_GeoFace(ptsIn, idxIn) result(this)
        implicit none
        type(GeoPoint), dimension(:), intent(in) :: ptsIn
        integer, dimension(:), intent(in) :: idxIn
        integer :: i, isize        
        
        isize = size(ptsIn)
        
        this%n = isize
        
        allocate(this%idx(isize))
        allocate(this%pts(isize))
        
        do i = 1, isize                        
            this%pts(i) = ptsIn(i)
            this%idx(i) = idxIn(i)
        end do
        
      end function new_GeoFace
    
    subroutine destructor(this)
        implicit none
        type(GeoFace) :: this        
        if (allocated(this % pts)) deallocate(this % pts)
        if (allocated(this % idx)) deallocate(this % idx)
    end subroutine    
      
 
end module GeoFace_Module

Fortran 没有内置对 List 数据结构的支持,无法动态地将新元素添加到未知大小的数组。部分原因是 Fortran 是 HPC(高性能计算)语言之一,主要用于数学计算,即使用大量的 Array 操作求解微分方程。Array 应该比基于 Array 构建的 List 更节省内存,访问速度更快,但 Array 的缺点是,在为其分配内存之前,需要知道它的大小或形状。在某些情况下,必须计算出最大的 Array 大小并在 Array 声明或分配中使用它。如果问题需要更灵活的 Array 元素操作,而不是 Array 元素值计算,那么 List 数据结构将是一个更好的选择。

这是实现 List 数据结构的 Utility 模块。

  • 方法 push 是将一个整数添加到一个一维整数数组。
  • 方法 push2d 是将一个固定大小的一维整数数组添加到一个二维整数数组。
  • 方法 list2dContains 是检查二维整数数组是否包含一维整数数组。

Utility.f03:

module Utility_Module
    
contains
    
    ! list: 1d array
    ! element: real number
    subroutine push(list, element)

        implicit none

        integer :: i, isize
        integer, intent(in) :: element
        integer, dimension(:), allocatable, intent(inout) :: list
        integer, dimension(:), allocatable :: clist

        if(allocated(list)) then
            isize = size(list)
            allocate(clist(isize+1))
            do i=1,isize          
                clist(i) = list(i)
            end do
            clist(isize+1) = element
            deallocate(list)
            call move_alloc(clist, list)
        else
            allocate(list(1))
            list(1) = element
        end if

    end subroutine push
        
    ! list: a 2d array
    ! element: a 1d array
    ! all element must have same size
    subroutine push2d(list, element)

        implicit none

        integer :: i, j, isize, esize
        integer, dimension(:), intent(in) :: element
        integer, dimension(:,:), allocatable, intent(inout) :: list
        integer, dimension(:,:), allocatable :: clist

        if(allocated(list)) then
            esize = size(element)
            isize = size(list)/esize;        

            allocate(clist(isize+1, esize))            

            do i=1,isize
                do j=1, esize
                    clist(i,j) = list(i,j)                    
                end do
            end do        
            
            do i=1, esize
                clist(isize+1, i) = element(i)                
            end do
                        
            deallocate(list)
            
            call move_alloc(clist, list)
        else
            esize = size(element)
            allocate(list(1, esize))
            do i=1,esize          
                list(1, i) = element(i)
            end do            
        end if

    end subroutine push2d
    
    subroutine sortArray(array)
        implicit none
        
        integer, dimension(:), intent(inout) :: array
        integer :: i, j, isize
        integer :: temp
                
        isize = size(array)
        
        if (isize .gt. 1) then
            do i = 1, isize - 1
                do j = i + 1, isize                
                    if(array(i) > array(j)) then
                        temp = array(j)
                        array(j) = array(i)
                        array(i) = temp        
                    end if
                end do
            end do
        end if        
    end subroutine sortArray
    
    ! check if 2d array list contains first esize elements in 1d array element
    ! esize <= size(element)
    logical function list2dContains(list, element, esize) result(isContains)

        implicit none

        integer :: i, j, isize                
        integer :: temp
        integer, dimension(:), allocatable :: tempListPart, tempElement    
        
        integer :: esize
        integer, dimension(:), intent(in) :: element
        integer, dimension(:,:), allocatable, intent(in) :: list       
                        
        isize = size(list)/esize            
        
        isContains = .false.                
        
        if ( (size(list) .ge. esize) .and. &
             (esize .gt. 1) .and. &
             (mod(size(list), esize) .eq. 0) ) then
            
            allocate(tempListPart(esize))
            allocate(tempElement(esize))
            
            do i=1, esize
                tempElement(i) = element(i)
            end do
                                    
            call sortArray(tempElement)
                        
            do i=1,isize
                                                
                do j=1, esize                    
                    tempListPart(j) = list(i, j)
                end do
                
                call sortArray(tempListPart)
                
                isContains = .true.
                
                do j=1, esize
                    if (tempListPart(j) .ne. tempElement(j)) then
                        isContains = .false.
                        exit
                    end if
                end do
                
                if(isContains) exit
                
            end do  
            
            deallocate(tempListPart)
            deallocate(tempElement)
            
        end if
                                
    end function list2dContains

end module Utility_Module

GeoPolygonProc.f03:

module GeoPolygonProc_Module                     
    use GeoPoint_Module
    use GeoVector_Module
    use GeoPlane_Module
    use GeoPolygon_Module
    use GeoFace_Module
    use Utility_Module
    
    implicit none
    
    real, parameter :: MaxUnitMeasureError = 0.001
    
    ! data member
    type GeoPolygonProc
        type(GeoPolygon) :: polygon
        real :: x0
        real :: x1
        real :: y0
        real :: y1
        real :: z0
        real :: z1
        real :: maxError
        type(GeoFace), dimension(:), allocatable :: Faces
        type(GeoPlane), dimension(:), allocatable :: FacePlanes
        integer :: NumberOfFaces
        contains
            procedure :: InitGeoPolygonProc
            procedure :: SetBoundary
            procedure :: SetMaxError
            procedure :: SetFacePlanes
            procedure :: PointInside3DPolygon
            procedure :: UpdateMaxError
    end type GeoPolygonProc
    
contains

    subroutine InitGeoPolygonProc(this, polygon)
        implicit none
        class(GeoPolygonProc) :: this
        
        type(GeoPolygon), intent(in) :: polygon        
    
        this%polygon = polygon
        
        call SetBoundary(this)
        call SetMaxError(this)
        call SetFacePlanes(this)
        
      end subroutine InitGeoPolygonProc
      
    subroutine SetBoundary(this)
        implicit none
        class(GeoPolygonProc) :: this
        integer :: i, n        
    
        n = this%polygon%n;

        this%x0 = this%polygon%pts(1)%x
        this%y0 = this%polygon%pts(1)%y
        this%z0 = this%polygon%pts(1)%z
        this%x1 = this%polygon%pts(1)%x
        this%y1 = this%polygon%pts(1)%y
        this%z1 = this%polygon%pts(1)%z        
        
        do i = 1, n
            if (this%polygon%pts(i)%x < this%x0) then
                this%x0 = this%polygon%pts(i)%x
            end if
            if (this%polygon%pts(i)%y < this%y0) then
                this%y0 = this%polygon%pts(i)%y
            end if
            if (this%polygon%pts(i)%z < this%z0) then
                this%z0 = this%polygon%pts(i)%z
            end if
            if (this%polygon%pts(i)%x > this%x1) then            
                this%x1 = this%polygon%pts(i)%x
            end if
            if (this%polygon%pts(i)%y > this%y1) then
                this%y1 = this%polygon%pts(i)%y
            end if
            if (this%polygon%pts(i)%z > this%z1) then
                this%z1 = this%polygon%pts(i)%z
            end if            
        end do
        
    end subroutine SetBoundary
    
    subroutine SetMaxError(this)
        implicit none
        class(GeoPolygonProc) :: this
        
        this%maxError = (abs(this%x0) + abs(this%x1) + &
                         abs(this%y0) + abs(this%y1) + &
                         abs(this%z0) + abs(this%z1)) / 6 * MaxUnitMeasureError;
                
    end subroutine SetMaxError
    
    subroutine SetFacePlanes(this)
        implicit none
        class(GeoPolygonProc) :: this
        
        logical :: isNewFace
        integer :: i, j, k, m, n, p, l, onLeftCount, onRightCount, &
                   numberOfFaces, maxFaceIndexCount
        real :: dis
        type(GeoPoint) :: p0, p1, p2, pt
        type(GeoPlane) :: trianglePlane, facePlane
        type(GeoFace) :: face
        type(GeoPoint), dimension(:), allocatable :: pts
        type(GeoPlane), dimension(:), allocatable :: fpOutward
        integer, dimension(:), allocatable :: &
            pointInSamePlaneIndex, verticeIndexInOneFace, faceVerticeIndexCount, idx
                
        ! vertices indexes 2d array for all faces,
        ! variable face index is major dimension, fixed total number of vertices as minor dimension
        ! vertices index is the original index value in the input polygon        
        integer, dimension(:,:), allocatable :: faceVerticeIndex     
        
        ! face planes for all faces defined with outward normal vector
        allocate(fpOutward(this%polygon%n))
                
        ! indexes of other points that are in same plane
        ! with the 3 face triangle plane point
        allocate(pointInSamePlaneIndex(this%polygon%n - 3))
        
        ! vertice indexes in one face
        maxFaceIndexCount = this%polygon%n -1
        allocate(verticeIndexInOneFace(maxFaceIndexCount))
        
        numberOfFaces = 0

        do i = 1, this%polygon%n
             
            ! triangle point #1
            p0 = this%polygon%pts(i)

            do j = i + 1, this%polygon%n
            
                ! triangle point #2
                p1 = this%polygon%pts(j)

                do k = j + 1, this%polygon%n
                                
                    ! triangle point #3
                    p2 = this%polygon%pts(k)

                    call trianglePlane%initGeoPlane(p0, p1, p2)                
                
                    onLeftCount = 0
                    onRightCount = 0
                    
                    m = 0
                    do l = 1, this%polygon%n
                             
                        ! any point except the 3 triangle points
                        if (l .ne. i .and. l .ne. j .and. l .ne. k) then
                        
                            pt = this%polygon%pts(l)

                            dis = trianglePlane * pt
                                                    
                            ! if point is in the triangle plane
                            ! add it to pointInSamePlaneIndex
                            if (abs(dis) .lt. this%maxError ) then    
                                m = m + 1                            
                                pointInSamePlaneIndex(m) = l                                
                            else                            
                                if (dis .lt. 0) then
                                    onLeftCount = onLeftCount + 1                           
                                else                                
                                    onRightCount = onRightCount + 1
                                end if
                            end if
                            
                        end if
                        
                    end do
                                
                    n = 0
                    do p = 1, maxFaceIndexCount
                        verticeIndexInOneFace(p) = -1
                    end do
                    
                    ! This is a face for a CONVEX 3d polygon.
                    ! For a CONCAVE 3d polygon, this maybe not a face.                    
                    if ((onLeftCount .eq. 0) .or. (onRightCount .eq. 0)) then
                                            
                        ! add 3 triangle plane point index
                        n = n + 1
                        verticeIndexInOneFace(n) = i
                        n = n + 1
                        verticeIndexInOneFace(n) = j
                        n = n + 1
                        verticeIndexInOneFace(n) = k
                                                                                
                        ! if there are other vertices in this triangle plane
                        ! add them to the face plane
                        if (m .gt. 0) then                        
                            do p = 1, m                
                                n = n + 1
                                verticeIndexInOneFace(n) = pointInSamePlaneIndex(p)
                            end do
                        end if
                
                        ! if verticeIndexInOneFace is a new face,
                        ! add it in the faceVerticeIndex list,
                        ! add the trianglePlane in the face plane list fpOutward
                        !print *, n, size(verticeIndexInOneFace), size(faceVerticeIndex)
                        isNewFace = .not. list2dContains(faceVerticeIndex, &
                                                 verticeIndexInOneFace, maxFaceIndexCount)
                        
                        if ( isNewFace ) then
                                                
                            numberOfFaces = numberOfFaces + 1    
                                                                                
                            call push2d(faceVerticeIndex, verticeIndexInOneFace)
                                                                                                            
                            if (onRightCount .eq. 0) then
                                fpOutward(numberOfFaces) = trianglePlane
                            else if (onLeftCount .eq. 0) then    
                                fpOutward(numberOfFaces) = -trianglePlane
                            end if
                            
                            call push(faceVerticeIndexCount, n)
                                                                                    
                        end if
                    
                    else
                                        
                        ! possible reasons:
                        ! 1. the plane is not a face of a convex 3d polygon,
                        !    it is a plane crossing the convex 3d polygon.
                        ! 2. the plane is a face of a concave 3d polygon
                    end if

                end do ! k loop
            end do ! j loop        
        end do ! i loop                        

        ! Number of Faces
        this%NumberOfFaces = numberOfFaces            
                            
        allocate(this%FacePlanes(this%NumberOfFaces))
        allocate(this%Faces(this%NumberOfFaces))
        
        ! loop faces
        do i = 1, this%NumberOfFaces
            
            ! set FacePlanes    
            this%FacePlanes(i) = GeoPlane(fpOutward(i)%a, fpOutward(i)%b, &
                                          fpOutward(i)%c, fpOutward(i)%d)
                                                     
            ! actual vertices count in the face
            n = faceVerticeIndexCount(i)
            
            allocate(pts(n))                        
            allocate(idx(n))
                                
            ! loop face vertices
            do j = 1, n
                                                    
                k = faceVerticeIndex(i, j)                                                
                pt = GeoPoint(this%polygon%pts(k)%x, this%polygon%pts(k)%y, this%polygon%pts(k)%z)
                                
                pts(j) = pt                                    
                idx(j) = k
                
            end do

            !set Faces            
            this%Faces(i) = GeoFace(pts, idx)            
            
            deallocate(pts)
            deallocate(idx)
            
        end do    
        
        deallocate(pointInSamePlaneIndex)
        deallocate(verticeIndexInOneFace)
        deallocate(faceVerticeIndex)
        deallocate(faceVerticeIndexCount)
        deallocate(fpOutward)
                
    end subroutine SetFacePlanes
    
    ! main function to be called. check if a point is inside 3d polygon
    logical function PointInside3DPolygon(this, x, y, z) result(ret)

        implicit none
        class(GeoPolygonProc) :: this
        
        real, intent(in) :: x, y, z                
        integer i
        real :: dis

        ! If the point is in the opposite half space with normal vector for all 6 faces,
        ! then it is inside of the 3D polygon
        ret = .true.
        
        do i = 1, this%NumberOfFaces                                        
            dis = this%FacePlanes(i) * GeoPoint(x, y, z)
            ! If the point is in the same half space with normal vector for any face of the 3D polygon,
            ! then it is outside of the 3D polygon        
            if (dis .gt. 0) then            
                ret = .false.
                exit
            end if
        end do
                    
    end function PointInside3DPolygon
    
    ! update maxError attribute valu of GeoPolygonProc object
    ! maxError is used in SetFacePlanes to threshold a maximum distance to
    ! check the points nearby the triangle plane if being considered to be inside the triangle plane
    ! maxError default value is calculated from polygon boundary in SetMaxError
    subroutine UpdateMaxError(this, maxError)
        implicit none
        class(GeoPolygonProc) :: this
        real, intent(in) :: maxError
        this%maxError = maxError
    end subroutine UpdateMaxError
           
end module GeoPolygonProc_Module

这是使用 GeoProc DLL 库的 LAS 处理程序。

从其他语言移植程序到 Fortran 时,请记住 Fortran 的起始索引是 1,许多其他语言使用 0 作为起始索引。这种差异需要更改文件 IO、数组索引、循环计数器等。这里有一个例子,SEEK 位置在其他语言中是 96,使用 0 作为起始索引,但 Fortran 使用 97 开始读取,对于 SEEK 位置 record_loc,Fortran 从 record_loc + 1 开始读取。

read(1, pos = 97) offset_to_point_data_value
...
read(1, pos = record_loc + 1) xi, yi, zi

LASProc.f03:

program LASProc
    
    use GeoPoint_Module
    use GeoVector_Module
    use GeoPlane_Module
    use GeoPolygon_Module
    use GeoFace_Module
    use Utility_Module
    use GeoPolygonProc_Module
    
    implicit none
    
    ! variables of GeoPolygonProc
    type(GeoPoint) :: p1, p2, p3, p4, p5, p6, p7, p8    
    type(GeoPoint), dimension(8) :: verticesArray    
    type(GeoPolygon) :: polygon         
    type(GeoPolygonProc) :: procObj
        
    ! variables of FILE IO
    integer (kind=4) :: xi, yi, zi
    integer (kind=4) :: offset_to_point_data_value, record_num    
    integer (kind=2) :: record_len    
    double precision :: x_scale, y_scale, z_scale, x_offset, y_offset, z_offset
    character, dimension(:), allocatable :: buf_header, buf_record
    
    ! local variables
    integer :: i, j, insideCount, record_loc
    real :: x, y, z
        
    ! get GeoPolygonProc object
    p1 = GeoPoint( - 27.28046,  37.11775,  - 39.03485)
    p2 = GeoPoint( - 44.40014,  38.50727,  - 28.78860)
    p3 = GeoPoint( - 49.63065,  20.24757,  - 35.05160)
    p4 = GeoPoint( - 32.51096,  18.85805,  - 45.29785)
    p5 = GeoPoint( - 23.59142,  10.81737,  - 29.30445)
    p6 = GeoPoint( - 18.36091,  29.07707,  - 23.04144)
    p7 = GeoPoint( - 35.48060,  30.46659,  - 12.79519)
    p8 = GeoPoint( - 40.71110,  12.20689,  - 19.05819)        
    verticesArray(1) = p1    
    verticesArray(2) = p2
    verticesArray(3) = p3
    verticesArray(4) = p4
    verticesArray(5) = p5
    verticesArray(6) = p6
    verticesArray(7) = p7
    verticesArray(8) = p8    
    polygon = GeoPolygon(verticesArray)
    call procObj%InitGeoPolygonProc(polygon);
    
    ! process LAS file
    
    open(unit=1, file="..\..\LASInput\cube.las", status="OLD", access="STREAM")
    open(unit=2, file="..\..\LASOutput\cube_inside.las", status="REPLACE", access="STREAM")
    open(unit=3, file="..\..\LASOutput\cube_inside.txt", status="REPLACE", action = "write")    
    
    ! Fortran Start Index is 1 while many of other languages Start Index is 0
    ! The code for Array, File IO, Loop, etc, need to change 
    ! when porting code from other language to Fortran.
    ! Here is the example, in C/C++, the SEEK position is 96, but in Fortran, 
    ! the read position is 97
    read(1, pos = 97) offset_to_point_data_value
    
    read(1, pos = 106) record_len
    read(1, pos = 108) record_num            
    read(1, pos = 132) x_scale, y_scale, z_scale, x_offset, y_offset, z_offset
        
    allocate(buf_header(offset_to_point_data_value))
    allocate(buf_record(record_len))
    
    ! read LAS header
    read(1, pos = 1) buf_header
    
    ! write LAS header
    write(2) buf_header
    
    insideCount = 0

    do i = 0, record_num - 1
        
        ! seek position
        record_loc = offset_to_point_data_value + record_len * i;
        
        ! Fortran Start Index is 1
        ! read position = seek position + 1
        read(1, pos = record_loc + 1) xi, yi, zi
        
        x = xi * x_scale + x_offset;
        y = yi * y_scale + y_offset;
        z = zi * z_scale + z_offset;
        
        if (x > procObj%x0 .and. x < procObj%x1 .and. &
            y > procObj%y0 .and. y < procObj%y1 .and. &
            z > procObj%z0 .and. z < procObj%z1 ) then
        
            if (procObj%PointInside3DPolygon(x, y, z)) then
            
                ! read LAS Point record
                read(1, pos = record_loc + 1) buf_record
                                
                ! write LAS Point record
                write(2) buf_record
                                
                ! write text LAS Point record
                write(3, fmt='(F15.6, F15.6, F15.6)') x, y, z

                insideCount = insideCount + 1
                
            end if
            
        end if
        
    end do
    
    ! update record_len in LAS header with actual writing record count    
    write(2, pos = 108) insideCount
        
    close(unit=1)
    close(unit=2)
    close(unit=3)
    
    deallocate(buf_header)
    deallocate(buf_record)

end program LASProc
       

这是一个所有 GeoProc DLL 库模块的模块测试程序。

test.f03:

program Test
    use GeoPoint_Module
    use GeoVector_Module
    use GeoPlane_Module
    use GeoPolygon_Module
    use GeoFace_Module
    use Utility_Module
    use GeoPolygonProc_Module
    
    implicit none
            
    type(GeoPoint) :: p1, p2, p3, p4, p5, p6, p7, p8, pt, insidePoint, outsidePoint        
    type(GeoVector) :: vt, v1, v2
    type(GeoPlane) :: pl
    real :: dis
    type(GeoPoint), dimension(8) :: verticesArray
    type(GeoPolygon) :: polygon     
    integer :: i, j, n
    type(GeoPoint), dimension(4) :: faceVerticesArray    
    integer, dimension(4) :: faceVerticeIndexArray
    type(GeoFace) :: face
    integer, dimension(:), allocatable :: arr1, arr2, arr3, arr4
    integer, dimension(:,:), allocatable :: arr2d
    logical :: b1, b2
    type(GeoPolygonProc) :: procObj
    
    
    p1 = GeoPoint( - 27.28046,  37.11775,  - 39.03485)
    p2 = GeoPoint( - 44.40014,  38.50727,  - 28.78860)
    p3 = GeoPoint( - 49.63065,  20.24757,  - 35.05160)
    p4 = GeoPoint( - 32.51096,  18.85805,  - 45.29785)
    p5 = GeoPoint( - 23.59142,  10.81737,  - 29.30445)
    p6 = GeoPoint( - 18.36091,  29.07707,  - 23.04144)
    p7 = GeoPoint( - 35.48060,  30.46659,  - 12.79519)
    p8 = GeoPoint( - 40.71110,  12.20689,  - 19.05819)
       
    print *, "GeoPoint Test:"    
    pt = p1 + p2    
    print *, pt%x, pt%y, pt%z
    
    print *, "GeoVector Test:"
    v1 = GeoVector(p1, p2)
    v2 = GeoVector(p1, p3)
    vt = v2 * v1            
    print *, get_x(vt), get_y(vt), get_z(vt)
    
    print *, "GeoPlane Test:"
    call pl%initGeoPlane(p1, p2, p3);
    print *, pl%a, pl%b, pl%c, pl%d    
    pl = GeoPlane(1.0, 2.0, 3.0, 4.0);
    print *, pl%a, pl%b, pl%c, pl%d    
    pl = -pl;
    print *, pl%a, pl%b, pl%c, pl%d    
    dis = pl * GeoPoint(1.0, 2.0, 3.0);
    print *, dis
    
    print *, "GeoPolygon Test:"    
    verticesArray(1) = p1    
    verticesArray(2) = p2
    verticesArray(3) = p3
    verticesArray(4) = p4
    verticesArray(5) = p5
    verticesArray(6) = p6
    verticesArray(7) = p7
    verticesArray(8) = p8    
    polygon = GeoPolygon(verticesArray)
    n = polygon%n    
    do i = 1, n
        print *, polygon%idx(i), ": (", polygon%pts(i)%x, ", ", polygon%pts(i)%y, ", ", polygon%pts(i)%z, ")"
    end do        
    
    print *, "GeoFace Test:"
    faceVerticesArray(1) = p1
    faceVerticesArray(2) = p2
    faceVerticesArray(3) = p3
    faceVerticesArray(4) = p4
    faceVerticeIndexArray = (/ 1, 2, 3, 4 /);    
    face = GeoFace(faceVerticesArray, faceVerticeIndexArray);
    n = face%n
    do i = 1, n
    print *, face%idx(i), ": (", face%pts(i)%x, ", ", face%pts(i)%y, ", ", face%pts(i)%z, ")"
    end do
    
    print *, "Utility Test:"     
    call push(arr1, 1)
    call push(arr1, 2)
    call push(arr1, 3)
    call push(arr1, 4)
    arr2 = (/ 4, 5, 6, 7 /)
    arr3 = (/ 2, 3, 1, 4, 6 /)
    arr4 = (/ 2, 3, 1, 5, 7 /)    
    call push2d(arr2d, arr1)
    call push2d(arr2d, arr2)            
    b1 = list2dContains(arr2d, arr3, 4)
    b2 = list2dContains(arr2d, arr4, 4)
    print *, b1, b2
    
    print *, "GeoPolygonProc Test:"    
    call procObj%InitGeoPolygonProc(polygon);
    print *, procObj%x0, procObj%x1
    print *, procObj%y0, procObj%y1
    print *, procObj%z0, procObj%z1
    print *, procObj%maxError
    print *, procObj%NumberOfFaces    
    print *, "Face Planes:";
    do i = 1, procObj%NumberOfFaces
        print *, i, ": ", procObj%FacePlanes(i)%a, procObj%FacePlanes(i)%b, &
                          procObj%FacePlanes(i)%c, procObj%FacePlanes(i)%d
    end do
    print *, "Faces:"
    do i = 1, procObj%NumberOfFaces    
        print *, "Face #", i, ":"
        do j = 1, procObj%Faces(i)%n
            print *, procObj%Faces(i)%idx(j), ": (", procObj%Faces(i)%pts(j)%x, &
                     procObj%Faces(i)%pts(j)%y, procObj%Faces(i)%pts(j)%z
        end do    
    end do
    insidePoint = GeoPoint(-28.411750, 25.794500, -37.969000)
    outsidePoint = GeoPoint(-28.411750, 25.794500, -50.969000)
    b1 = procObj%PointInside3DPolygon(insidePoint%x, insidePoint%y, insidePoint%z)
    b2 = procObj%PointInside3DPolygon(outsidePoint%x, outsidePoint%y, outsidePoint%z)    
    print *, b1, ", ", b2
    
end program Test

这是模块测试程序的输出。

 GeoPoint Test:
  -71.6806030       75.6250153      -67.8234558
 GeoVector Test:
  -178.390884       160.813675      -319.868134
 GeoPlane Test:
  -178.390884       160.813675      -319.868134      -23321.6328
   1.00000000       2.00000000       3.00000000       4.00000000
  -1.00000000      -2.00000000      -3.00000000      -4.00000000
  -18.0000000
 GeoPolygon Test:
           1 : (  -27.2804604     ,    37.1177483     ,   -39.0348511     )
           2 : (  -44.4001389     ,    38.5072708     ,   -28.7886009     )
           3 : (  -49.6306496     ,    20.2475700     ,   -35.0516014     )
           4 : (  -32.5109596     ,    18.8580494     ,   -45.2978516     )
           5 : (  -23.5914192     ,    10.8173704     ,   -29.3044491     )
           6 : (  -18.3609104     ,    29.0770702     ,   -23.0414391     )
           7 : (  -35.4805984     ,    30.4665909     ,   -12.7951899     )
           8 : (  -40.7111015     ,    12.2068901     ,   -19.0581894     )
 GeoFace Test:
           1 : (  -27.2804604     ,    37.1177483     ,   -39.0348511     )
           2 : (  -44.4001389     ,    38.5072708     ,   -28.7886009     )
           3 : (  -49.6306496     ,    20.2475700     ,   -35.0516014     )
           4 : (  -32.5109596     ,    18.8580494     ,   -45.2978516     )
 Utility Test:
 T F
 GeoPolygonProc Test:
  -49.6306496      -18.3609104
   10.8173704       38.5072708
  -45.2978516      -12.7951899
   2.92348750E-02
           6
 Face Planes:
           1 :   -178.390884       160.813675      -319.868134      -23321.6328

           2 :    104.610008       365.194000       125.259911      -5811.86768

           3 :    342.393494      -27.7903938      -204.924881       2372.95679

           4 :   -342.393677       27.7906227       204.925003      -10372.9639

           5 :   -104.609970      -365.193939      -125.260048      -2188.13623

           6 :    178.390854      -160.813873       319.868256       15321.6396

 Faces:
 Face #           1 :
           1 : (  -27.2804604       37.1177483      -39.0348511
           2 : (  -44.4001389       38.5072708      -28.7886009
           3 : (  -49.6306496       20.2475700      -35.0516014
           4 : (  -32.5109596       18.8580494      -45.2978516
 Face #           2 :
           1 : (  -27.2804604       37.1177483      -39.0348511
           2 : (  -44.4001389       38.5072708      -28.7886009
           6 : (  -18.3609104       29.0770702      -23.0414391
           7 : (  -35.4805984       30.4665909      -12.7951899
 Face #           3 :
           1 : (  -27.2804604       37.1177483      -39.0348511
           4 : (  -32.5109596       18.8580494      -45.2978516
           5 : (  -23.5914192       10.8173704      -29.3044491
           6 : (  -18.3609104       29.0770702      -23.0414391
 Face #           4 :
           2 : (  -44.4001389       38.5072708      -28.7886009
           3 : (  -49.6306496       20.2475700      -35.0516014
           7 : (  -35.4805984       30.4665909      -12.7951899
           8 : (  -40.7111015       12.2068901      -19.0581894
 Face #           5 :
           3 : (  -49.6306496       20.2475700      -35.0516014
           4 : (  -32.5109596       18.8580494      -45.2978516
           5 : (  -23.5914192       10.8173704      -29.3044491
           8 : (  -40.7111015       12.2068901      -19.0581894
 Face #           6 :
           5 : (  -23.5914192       10.8173704      -29.3044491
           6 : (  -18.3609104       29.0770702      -23.0414391
           7 : (  -35.4805984       30.4665909      -12.7951899
           8 : (  -40.7111015       12.2068901      -19.0581894
 T ,  F

编译

使用 GNU Fortran 编译器

编译 DLL 库 GeoProc.dll

gfortran -c .\src\Utility.f03
gfortran -c .\src\GeoPoint.f03
gfortran -c .\src\GeoVector.f03 -I.
gfortran -c .\src\GeoPlane.f03 -I.
gfortran -c .\src\GeoPolygon.f03 -I.
gfortran -c .\src\GeoFace.f03 -I.
gfortran -c .\src\GeoPolygonProc.f03 -I.
gfortran -shared -mrtd -o GeoProc.dll *.o

编译测试程序

gfortran -o test.exe test.f03 -I..\GeoProc\module -L..\GeoProc\lib -lGeoProc

编译 LAS 处理程序

gfortran -o LASProc.exe LASProc.f03 -I..\GeoProc\module -L..\GeoProc\lib -lGeoProc

历史

  • 2016年2月10日:初始版本日期

参考文献

历史

  • 2016年2月10日:初始版本
© . All rights reserved.