//
//  LocationCalculation.swift
//  jmarine
//
//  Created by chihirohirakawa on 2020/05/20.
//  Copyright © 2020 J-MarineCloud. All rights reserved.
//

import Foundation
import CoreLocation
// import Mapbox

struct PointC {
    var x: Int
    var y: Int
}

class LocationCalculation{
    static let nm = 1852.0                 // 国際海里 1852.0[m]
    static let DI_BIAS = 10000000.0        //計算精度を1/100000°（暫定的、多分今後のGPS測位精度から推測しても十分なはず
    static let ACCY_ROOT = 0.0000001
    static let ACCY_COORD = 0.001
    static let ACCY_RAD = 0.000001
    static let ACCY_DEG = 0.0001

    /**
     * NMをkmに変換
     */
    static func nm2km(nm: Double) -> Double{
        return nm * 1.852
    }

    /**
     * kmをNMに変換
     */
    static func km2nm(km: Double) -> Double{
        return km / 1.852
    }

    static func kn2kmh(kn: Double) -> Double{
        return  kn * 1.852
    }

    static func deg2rad(deg: Double) -> Double {
        return (deg / 180.0) * Double.pi
    }

    static func rad2deg(rad: Double) -> Double {
        return (rad / Double.pi) * 180.0
    }

    /**
     * 地理座標の確認
     */
    static func checkPosition(pos: CLLocationCoordinate2D) -> Bool { // ENvCheckPos
        return pos.latitude >= -90.0 && pos.latitude <= 90.0 && pos.longitude >= -180.0 && pos.longitude <= 180.0
    }

    /**
     * 角度の正規化
     */
    static func rangeDegree(degree: Double) -> Double {
        var rangeDegree = degree
        while (rangeDegree >= 360.0) {
            rangeDegree -= 360.0
        }

        while (rangeDegree < 0.0) {
            rangeDegree += 360.0
        }

        if (rangeDegree == 360.0) {
            rangeDegree = 0.0
        }

        return rangeDegree
    }

    /// 2点間距離の算出
    /// - Parameters:
    ///   - posAlat: 地点A緯度
    ///   - posAlon: 地点A経度
    ///   - posBlat: 地点B緯度
    ///   - posBlon: 地点B経度
    /// - Returns:
    ///   単位：メートル
    static func distance(posAlat:Double, posAlon:Double, posBlat:Double, posBlon:Double) -> Double {
        //2点の緯度の平均
        let latAvg = deg2rad(deg: posAlat + ((posBlat - posAlat) / 2.0))
        //2点の緯度差
        let latDifference = deg2rad(deg: posAlat - posBlat)
        //2点の経度差
        let lonDifference = deg2rad(deg: posAlon - posBlon)

        let curRadiusTemp = 1 - 0.00669438 * pow(sin(latAvg), 2.0)

        //子午線曲率半径
        let meridianCurvatureRadius = 6335439.327 / sqrt(pow(curRadiusTemp, 3.0))
        //卯酉線曲率半径
        let primeVerticalCircleCurvatureRadius = 6378137 / sqrt(curRadiusTemp)

        //2点間距離
        let distanceTemp = pow(meridianCurvatureRadius * latDifference, 2.0) + pow(primeVerticalCircleCurvatureRadius * cos(latAvg) * lonDifference, 2.0)
        return sqrt(distanceTemp) //メートル単位
    }
    /// 2点間距離の算出
    /// - Parameters:
    ///   - posA: 地点A
    ///   - posB: 地点B
    /// - Returns:
    ///   単位：メートル
    static func distance(posA: CLLocationCoordinate2D, posB: CLLocationCoordinate2D) -> Double {
        return distance(posAlat: posA.latitude, posAlon: posA.longitude, posBlat: posB.latitude, posBlon: posB.longitude)
    }

    /// 地点Bから地点Aをみたときの方位角
    /// - Parameters:
    ///   - posAlat: 地点A緯度
    ///   - posAlon: 地点A経度
    ///   - posBlat: 地点B緯度
    ///   - posBlon: 地点B経度
    /// - Returns:
    ///   単位：度
    static func direction(posAlat:Double, posAlon:Double, posBlat:Double, posBlon:Double) -> Double {
        let posA_lat = deg2rad(deg: posAlat)
        let posA_lon = deg2rad(deg: posAlon)
        let posB_lat = deg2rad(deg: posBlat)
        let posB_lon = deg2rad(deg: posBlon)

        //経度の中心地
        let posC_lat = (posA_lat + posB_lat) / 2.0
        let dx = 6378137 * (posB_lon - posA_lon) * cos(posC_lat)
        let dy = 6378137 * (posB_lat - posA_lat)

        if(dx == dy){
            return 0.0
        }

        //なぜか左向きが０度、反時計回りに増える
        let deg = rad2deg(rad: atan2(dy, dx))

        //それを修正
        let deg_o = deg + 90
        let deg_p = 360 - deg_o

        if(deg_p >= 360) {
            return deg_p - 360
        } else{
            return deg_p
        }
    }

    /// 起点から指定の距離方位分移動した地点の緯度経度
    /// - Parameters:
    ///   - startPointLat: 起点緯度
    ///   - startPointLon: 起点経度
    ///   - initialBearing: 方位
    ///   - distanceKilometers: 距離（キロメートル指定）
    /// - Returns:
    ///   移動した地点の緯度経度
    static func findPointAtDistanceFrom(
        startPointLat:Double,
        startPointLon:Double,
        initialBearing:Double,
        distanceKilometers:Double) -> CLLocationCoordinate2D {

            let initialBearingRadians = initialBearing * 2.0 * Double.pi / 360.0
            let radiusEarthKilometers = 6371.01
            let distRatio = distanceKilometers / radiusEarthKilometers
            let distRatioSine = sin(distRatio)
            let distRatioCosine = cos(distRatio)

            let startLatRad = deg2rad(deg: startPointLat)
            let startLonRad = deg2rad(deg: startPointLon)

            let startLatCos = cos(startLatRad)
            let startLatSin = sin(startLatRad)

            let endLatRads = asin((startLatSin * distRatioCosine) + (startLatCos * distRatioSine * cos(initialBearingRadians)))

            let endLonRads = startLonRad + atan2(sin(initialBearingRadians) * distRatioSine * startLatCos, distRatioCosine - startLatSin * sin(endLatRads))

            return CLLocationCoordinate2D(latitude: rad2deg(rad: endLatRads), longitude: rad2deg(rad: endLonRads))
        }

    /// 地点Aと地点Bをの間の位置
    /// - Parameters:
    ///   - posAlat: 地点A緯度
    ///   - posAlon: 地点A経度
    ///   - posBlat: 地点B緯度
    ///   - posBlon: 地点B経度
    /// - Returns:
    ///   ２地点間の中間緯度経度
    static func halfPoint(posAlat:Double, posAlon:Double, posBlat:Double, posBlon:Double) -> CLLocationCoordinate2D {

        let lat = ( posAlat + posBlat ) / 2
        let lon = ( posAlon + posBlon ) / 2

        return CLLocationCoordinate2D(latitude: lat, longitude: lon)
    }

    static func halfPoint(posA:CLLocationCoordinate2D, posB:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
        return CLLocationCoordinate2D(latitude: ((posA.latitude + posB.latitude) / 2), longitude: ((posA.longitude + posB.longitude) / 2))
    }

    /// Plygonの中心の位置
    /// - Parameters:
    ///   - positions: 地点配列
    /// - Returns:
    ///   Polygonの中間緯度経度
    static func CenterPoint(positions : [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
        var latMax : Double = 0
        var latMin : Double = 360
        var lonMax : Double = 0
        var lonMin : Double = 360
        for position in positions {
            if latMax < position.latitude{
                latMax = position.latitude
            }
            if latMin > position.latitude{
                latMin = position.latitude
            }
            if lonMax < position.longitude{
                lonMax = position.longitude
            }
            if lonMin > position.longitude{
                lonMin = position.longitude
            }
        }
        let lat = ( latMax + latMin ) / 2
        let lon = ( lonMax + lonMin ) / 2

        return CLLocationCoordinate2D(latitude: lat, longitude: lon)
    }

    static func CenterPoint2(positions : [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
        var Max: CLLocationCoordinate2D = .init(latitude: 0, longitude: 0)
        var Min: CLLocationCoordinate2D = .init(latitude: 0, longitude: 0)

        Max = positions[0]
        Min = positions[0]

        for position in positions {
            if fabs(Max.latitude) < fabs(position.latitude) {
                Max.latitude = position.latitude
            }
            if fabs(Min.latitude) > fabs(position.latitude) {
                Min.latitude = position.latitude
            }
            if fabs(Max.longitude) < fabs(position.longitude) {
                Max.longitude = position.longitude
            }
            if fabs(Min.longitude) > fabs(position.longitude) {
                Min.longitude = position.longitude
            }
        }

        var ret = CLLocationCoordinate2D()
        ret.latitude = (Max.latitude + Min.latitude) / 2
        ret.longitude = (Max.longitude + Min.longitude) / 2

        return ret
    }

    /// 緯度を数値から文字列に変換
    /// - Parameters:
    ///   - lat: 緯度数値
    /// - Returns
    ///  緯度文字列
    static func latDegtoString(lat: Double) -> String {
        let latNS : String
        if (lat >= 0){
            latNS = "N"
        } else {
            latNS = "S"
        }

        let latNum : Double
        if (lat >= 0) {
            latNum = lat
        }else{
            latNum = -lat
        }

        let latDeg = Int32(latNum)
        let latMin = (latNum - Double(latDeg)) * 60.0

        return "\(latDeg)°\(String(format:"%06.3f", latMin))′\(latNS)"
    }



    /// 経度を数値から文字列に変換
    /// - Parameters:
    ///   - lon: 経度数値
    /// - Returns
    ///  経度文字列
    static func lonDegtoString(lon: Double) -> String {
        let lonEW : String
        if (lon >= 0){
            lonEW = "E"
        }else{
            lonEW = "W"
        }

        let lonNum : Double
        if (lon >= 0){
            lonNum = lon
        } else {
            lonNum = -lon
        }

        let lonDeg = Int32(lonNum)
        let lonMin = (lonNum - Double(lonDeg)) * 60.0

        return "\(lonDeg)°\(String(format:"%06.3f", lonMin))′\(lonEW)"
    }

    /**
     * 緯度経度を数値から文字列に変換
     */
    static func locationDegtoString(location: CLLocationCoordinate2D) -> String {
        let latNS : String
        if (location.latitude >= 0){
            latNS = "N"
        } else {
            latNS = "S"
        }

        let latNum : CLLocationDegrees
        if (location.latitude >= 0) {
            latNum = location.latitude
        }else{
            latNum = -location.latitude
        }

        let latDeg = Int32(latNum)
        let latMin = (latNum - CLLocationDegrees(latDeg)) * 60.0

        let lonEW : String
        if (location.longitude >= 0){
            lonEW = "E"
        }else{
            lonEW = "W"
        }

        let lonNum : CLLocationDegrees
        if (location.longitude >= 0){
            lonNum = location.longitude
        } else {
            lonNum = -location.longitude
        }

        let lonDeg = Int32(lonNum)
        let lonMin = (lonNum - CLLocationDegrees(lonDeg)) * 60.0

        return "\(String(format:"%3d", latDeg))°\(String(format:"%06.3f", latMin))′\(latNS)\n\(String(format:"%03d", lonDeg))°\(String(format:"%06.3f", lonMin))′\(lonEW)"
    }

    /**
     * 緯度の文字列を数値に変換
     * NXX°XX.XX
     */
    static func latDegtoDouble(lat: String) -> Double? {
        var number: String
        var direction: Double
        if (!lat.contains("N")) {
            number = lat.replacingOccurrences(of: "S", with: "")
            direction = -1.0
        } else {
            number = lat.replacingOccurrences(of: "N", with: "")
            direction = 1.0
        }

        let min = number.components(separatedBy: ".")
        if min.count < 1{
            return nil
        }
        let deg = min[0].components(separatedBy: "°")
        if deg.count < 2{
            return nil
        }
        if let deg0 = Double(deg[0]),
           let deg1 = Double(deg[1]),
           let min1Str = min[1].components(separatedBy: "′").first,
           let min1 = Double(min1Str)
        {
            let decimal = (deg1 + min1 / pow(10.0, Double(min1Str.count))) / 60.0
            return ((deg0 + decimal) * direction)
        }else{
            return nil
        }
    }

    /**
     * 経度の文字列を数値に変換
     * EXXX°XX.XX
     */
    static func lonDegtoDouble(lon: String) -> Double? {
        var number: String
        var direction: Double
        if (!lon.contains("E")) {
            number = lon.replacingOccurrences(of: "W", with: "")
            direction = -1.0
        } else {
            number = lon.replacingOccurrences(of: "E", with: "")
            direction = 1.0
        }

        let min = number.components(separatedBy: ".")
        if min.count < 1{
            return nil
        }
        let deg = min[0].components(separatedBy: "°")
        if deg.count < 2{
            return nil
        }
        if let deg0 = Double(deg[0]),
           let deg1 = Double(deg[1]),
           let min1Str = min[1].components(separatedBy: "′").first,
           let min1 = Double(min1Str)
        {
            let decimal = (deg1 + min1 / pow(10.0, Double(min1Str.count))) / 60.0
            return ((deg0 + decimal) * direction)
        }else{
            return nil
        }
    }

    /// 緯度の数値を分解
    /// - Parameters:
    ///   - lat: 緯度数値
    /// - Returns
    ///  分解した数値
    static func latDegtoInt(lat: CLLocationDegrees) -> [Int] {
        var disLat : [Int] = []
        if (lat >= 0){
            disLat.append(0)    //N
        } else {
            disLat.append(1)    //S
        }

        let latNum : Double
        if (lat >= 0) {
            latNum = lat
        }else{
            latNum = -lat
        }
        let latDeg = Int(latNum)
        let latMin = (latNum - Double(latDeg)) * 60.0
        let latM = Int(latMin)
        let latL = Int((latMin - Double(latM)) * 1000.0)
        disLat.append(latDeg)
        disLat.append(latM)
        disLat.append(latL)

        return disLat
    }

    /// 経度の数値を分解
    /// - Parameters:
    ///   - lon: 経度数値
    /// - Returns
    ///  分解した数値
    static func lonDegtoInt(lon: CLLocationDegrees) -> [Int] {
        var disLat : [Int] = []
        if (lon >= 0){
            disLat.append(0)    //E
        }else{
            disLat.append(1)    //W
        }

        let lonNum : Double
        if (lon >= 0){
            lonNum = lon
        } else {
            lonNum = -lon
        }
        let lonDeg = Int(lonNum)
        let lonMin = (lonNum - Double(lonDeg)) * 60.0
        let lonM = Int(lonMin)
        let lonL = Int((lonMin - Double(lonM)) * 1000.0)
        disLat.append(lonDeg)
        disLat.append(lonM)
        disLat.append(lonL)

        return disLat
    }

    /**
     * 緯度の配列を数値に変換
     * NXX°XX.XX
     */
    static func latInttoDouble(lat: [Int]) -> CLLocationDegrees? {
        if lat.count != 4 {
            return nil
        }

        var direction : CLLocationDegrees
        if lat[0] == 0 { //N
            direction = 1.0
        } else {
            direction = -1.0
        }

        let deg0 : CLLocationDegrees = CLLocationDegrees(lat[1])
        let deg1 : CLLocationDegrees = CLLocationDegrees(lat[2])
        let min1 : CLLocationDegrees = CLLocationDegrees(lat[3])

        let decimal = (deg1 + min1 / 1000) / 60.0
        return ((deg0 + decimal) * direction)
    }

    /**
     * 経度の配列を数値に変換
     * EXXX°XX.XX
     */
    static func lonInttoDouble(lon: [Int]) -> CLLocationDegrees? {
        if lon.count != 4 {
            return nil
        }

        var direction : CLLocationDegrees
        if lon[0] == 0 {   //E
            direction = 1.0
        } else {
            direction = -1.0
        }

        let deg0 : CLLocationDegrees = CLLocationDegrees(lon[1])
        let deg1 : CLLocationDegrees = CLLocationDegrees(lon[2])
        let min1 : CLLocationDegrees = CLLocationDegrees(lon[3])

        let decimal = (deg1 + min1 / 1000) / 60.0
        return ((deg0 + decimal) * direction)
    }

    ///時計回りか
    ///return: 1 -> 時計回り
    ///     -1 -> 反時計回り
    ///     null -> 直線or点
    static func clockWise(obj: [CLLocationCoordinate2D]) -> Int?{
        if obj.count < 3{
            //3点未満であれば直線or点
            return nil
        }else{
            var s = 0.0
            for i in 0...(obj.count - 2){
                s += ((obj[i].longitude * obj[i + 1].latitude) - (obj[i + 1].longitude * obj[i].latitude))
            }
            if s > 0 {
                return -1
            }else if s < 0{
                return 1
            }else{
                return nil
            }
        }
    }

    /// 地点Bから地点Aをみたときの方位角文字列
    /// - Parameters:
    ///   - posAlat: 地点A緯度
    ///   - posAlon: 地点A経度
    ///   - posBlat: 地点B緯度
    ///   - posBlon: 地点B経度
    /// - Returns:
    ///   単位：度
    static func directionStr(posAlat:Double, posAlon:Double, posBlat:Double, posBlon:Double) -> String {
        let direction = direction(posAlat: posAlat, posAlon: posAlon, posBlat: posBlat, posBlon: posBlon)
        return String(round(direction)) + "°"

    }

    ///距離（NM）を文字列に変換
    static func distance2Str(nm: Double) -> String{
        var rtnStr = ""
        if nm < 0.1{
            rtnStr = String(format:"%.2f", nm) + "NM"
        }else if nm < 1000{
            rtnStr = String(format:"%.1f", nm) + "NM"
        }else{
            rtnStr = String(Int(nm)) + "NM"
        }
        return rtnStr
    }

    static let rad45 = 0.785398163397448   // radで45°
    static let rDeg = 57.295779513082323   // radからdegに変換する乗数
    static let eEccentricity = 0.08181919  // 離心率
    static let brg = 0.001                 // 方位精度
    static let deg2NM = 60.0
    static let pidDiv2 = Double.pi / 2.0
    static let errorRange = 0.00001        //誤差範囲

    struct AreaLatLon {
        var NorthLat: CLLocationDegrees = 0.0
        var SouthLat: CLLocationDegrees = 0.0
        var EastLon: CLLocationDegrees = 0.0
        var WestLon: CLLocationDegrees = 0.0
    }

    /**
     * 包囲チェック
     * 1)直線X=CXとオブジェクトの各辺との交点を求め、交点が検索領域の中心CYより上方にあるもの
     *   下方にあるものをそれぞれカウントし、それぞれが奇数個ならば、検索対象オブジェクトが検索領域を包囲していると判断する
     * 2)始点、終点がともにX=CX上に存在する場合、つまり、X=CXと重なる垂直線の場合はカウントしない
     * 3)終点のみX=CX上にある場合はカウントし、次の直線がX=CXと重ならない場合で、終点がCXを中心にして、
     *   前の始点と同じ方向にある場合はカウントし、逆の方向にある場合はカウントしない
     *
     * MFD /src/Chart/DecisionAlgorithm.cpp CheckBeset移植
     */
    static func checkBeset(
        objPos: Array<CLLocationCoordinate2D>,
        minMax: AreaLatLon,
        exceed180: Bool)
    -> Bool
    {
        var lUpperCnt = 0
        var lLowerCnt = 0
        var lBeforeLR = 0                   //-1:Left 1:Right
        var lpSP = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)    //180度回り込み経度換算処理後の始点
        var lpEP = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)    //180度回り込み経度換算処理後の終点
        let cx = (minMax.EastLon + minMax.WestLon) / 2.0
        let cy = (minMax.NorthLat + minMax.SouthLat) / 2.0

        let max = objPos.count - 2
        for i in 0...max {
            //180度回り込みチェック用のワークエリアに移す
            lpSP = objPos[i]
            lpEP = objPos[(i + 1)]

            //180度回り込み経度換算処理 (TransLonの中で検査領域minMaxの180度回り込みもチェック)
            if lpSP.longitude > lpEP.longitude {
                if ((lpEP.longitude < 0.0) && (lpSP.longitude > 0.0))
                    && ((lpEP.longitude + 180.0) < lpSP.longitude) {  //Objectの直線が180度を跨っている時
                    //最大、最小を入れ替えて、経度を換算する
                    let check = transLon(max: lpEP, min: lpSP, minMax: minMax, exceed180: true, exceed180Search: exceed180)
                    lpEP = check.first
                    lpSP = check.second
                } else {
                    let check = transLon(max: lpSP, min: lpEP, minMax: minMax, exceed180: false, exceed180Search: exceed180)
                    lpSP = check.first
                    lpEP = check.second
                }
            } else {
                if ((lpSP.longitude < 0.0) && (lpEP.longitude > 0.0))
                    && ((lpSP.longitude + 180.0) < lpEP.longitude) {   //Objectの直線が180度を跨っている時
                    //最大、最小を入れ替えて、経度を換算する
                    let check = transLon(max: lpSP, min: lpEP, minMax: minMax, exceed180: true, exceed180Search: exceed180)
                    lpSP = check.first
                    lpEP = check.second
                } else {
                    let check = transLon(max: lpEP, min: lpSP, minMax: minMax, exceed180: false, exceed180Search: exceed180)
                    lpEP = check.first
                    lpSP = check.second
                }
            }

            var maxX: CLLocationDegrees
            if lpEP.longitude > lpSP.longitude {
                maxX = lpEP.longitude
            } else {
                maxX = lpSP.longitude
            }
            var minX: CLLocationDegrees
            if lpEP.longitude < lpSP.longitude {
                minX = lpEP.longitude
            } else {
                minX = lpSP.longitude
            }
            if (maxX < cx) || (minX > cx) {
                //中心線と交わらない場合
                continue
            }
            if lpEP.longitude == lpSP.longitude {
                //垂直線の場合
                continue
            }

            if lpEP.latitude == lpSP.latitude { //平行線の場合
                if lpSP.longitude == cx {  //始点が中心線上にある場合
                    //終点がその前の線分の始点と同じ方向か？
                    if (lpEP.longitude == maxX && lBeforeLR == 1)
                        || (lpEP.longitude == minX && lBeforeLR == -1) {
                        //同じならばカウントする
                        if lpSP.latitude > cy {
                            lUpperCnt += 1
                        } else {
                            lLowerCnt += 1
                        }
                        lBeforeLR = 0
                    }
                } else if lpEP.longitude == cx {   //終点が中心線上にある場合
                    //カウントする
                    if lpEP.latitude > cy {
                        lUpperCnt += 1
                    } else {
                        lLowerCnt += 1
                    }
                    if lpSP.longitude == maxX {
                        lBeforeLR = 1   //線分の始点が中心より右
                    } else {
                        lBeforeLR = -1  //線分の始点が中心より左
                    }
                } else {    //X=CXと交わる平行線なので、カウントする
                    if (lpSP.latitude > cy) {
                        lUpperCnt += 1
                    } else {
                        lLowerCnt += 1
                    }
                }
                continue
            }

            //直線S-E Y=(Ey-Sy)/Ex-Sx)*X+(Sy*Ex-Sx*Ey)/(Ex-Sx)
            //交点のY座標、Y
            let Y = (lpEP.latitude - lpSP.latitude) / (lpEP.longitude - lpSP.longitude) * cx +
            (lpSP.latitude * lpEP.longitude - lpSP.longitude * lpEP.latitude) /
            (lpEP.longitude - lpSP.longitude)
            if lpSP.longitude == cx {   //始点が中心線上にある場合
                //終点がその前の線分の始点と同じ方向か?
                if (lpEP.longitude == maxX && lBeforeLR == 1)
                    || (lpEP.longitude == minX && lBeforeLR == -1)
                {
                    //同じならばカウントする
                    if lpSP.latitude > cy {
                        lUpperCnt += 1
                    } else {
                        lLowerCnt += 1
                    }
                }
                lBeforeLR = 0
            } else if lpEP.longitude == cx {    //終点が中心線上にある場合
                if (lpEP.latitude > cy) {
                    lUpperCnt += 1
                } else {
                    lLowerCnt += 1
                }
                if lpSP.longitude == maxX {
                    lBeforeLR = 1   //線分の始点が中心より右
                } else {
                    lBeforeLR = -1  //線分の始点が中心より左
                }
            } else {
                //X=CXと交わる線分なので、カウントする
                if Y > cy {
                    lUpperCnt += 1
                } else {
                    lLowerCnt += 1
                }
            }
        }

        if lUpperCnt % 2 == 1 && lLowerCnt % 2 == 1 {
            return true
        } else {
            return false
        }
    }

    /**
     * 180度跨りを考慮した直線の経度の変換
     * 2点を受け取り、検索領域が180度を跨っているかどうかによって2点の経度を変換する
     */
    static func transLon(
        max: CLLocationCoordinate2D,
        min: CLLocationCoordinate2D,
        minMax: AreaLatLon,
        exceed180: Bool,
        exceed180Search: Bool)
    -> (first: CLLocationCoordinate2D, second: CLLocationCoordinate2D)
    {
        var maxRt = max
        var minRt = min

        //Objectの直線が180度を跨っている時は最小、最大を入れ替え済み
        if exceed180Search {  //検索領域が180度を跨っている時
            if min.longitude < 0.0  { //最小経度が-ならば、最小、最大経度に360度をプラスする
                minRt.longitude = min.longitude + 360.0
                maxRt.longitude = max.longitude + 360.0
            } else if max.longitude < 0.0 {
                maxRt.longitude = max.longitude + 360.0
            }
        } else {    //検索領域は180度を跨っていない
            if exceed180 { //Objectの直線が180度を跨っている時
                if minMax.WestLon > 0.0 { //検索領域の経度が+
                    maxRt.longitude = max.longitude + 360.0
                } else {    //検索領域の経度が-
                    minRt.longitude = min.longitude - 360.0
                }
            }
        }
        return (maxRt, minRt)
    }

    enum Mrdc {
        case E_NV_MRDC_NORMAL
        case E_NV_MRDC_XTE_R
    }

    struct ShipPosInf {
        var retCode: Bool = false   //正常終了:true 異常(位置異常):false
        var co1: Double = 0.0       //前回位置から次回位置への方位[deg]
        var co2: Double = 0.0       //次回位置から前回位置への方位[deg]
        var dist: Double = 0.0      //前回位置から次回位置までの距離[nm]
    }

    struct DistanceInf {
        var retCode: Bool = false   //正常終了:true 異常(位置異常):false
        var dist: Double = 0.0      //前回位置から次回位置までの距離[nm]
    }

    struct BearingInf {
        var retCode: Bool = false   //正常終了:true 異常(位置異常):false
        var co1: Double = 0.0       //前回位置から次回位置への方位[deg]
        var co2: Double = 0.0       //次回位置から前回位置への方位[deg]
    }

    /**
     * 線跨ぎ確認用処理
     */
    static func checkCrossLine(
        lastPos: CLLocationCoordinate2D,    //レグ起点
        nextPos: CLLocationCoordinate2D,    //レグ終点
        shipPos: CLLocationCoordinate2D)    //自船位置
    -> (retCode: Bool, xte: Double) //正常終了:true 異常:false 距離[NM]
    {
        let deg1 = bearing(lastPos: lastPos, nextPos: nextPos)    //レグ起点 → レグ終点
        let deg2 = bearing(lastPos: lastPos, nextPos: shipPos)    //レグ起点 → 自船位置
        let deg3 = bearing(lastPos: nextPos, nextPos: shipPos)    //レグ終点 → 自船位置

        //自船を頂点とした方位が９０度以内
        if (deg1.retCode && deg2.retCode && deg3.retCode) {
            let brg1 = abs(deg1.co1 - deg2.co1)
            let brg2 = abs(deg3.co1 - deg1.co2)
            if (brg1 <= 90.0 && brg2 <= 90.0) {
                let rtn = eNvXteRL(lastPos: lastPos, nextPos: nextPos, shipPos: shipPos)
                if (rtn.retCode) {
                    return (true, rtn.xte)
                }
            }

            let brg3 = abs(deg1.co2 - deg2.co2)
            let brg4 = abs(deg3.co2 - deg1.co1)
            if (brg3 <= 90.0 && brg4 <= 90.0) {
                let rtn = eNvXteRL(lastPos: lastPos, nextPos: nextPos, shipPos: shipPos)
                if (rtn.retCode) {
                    return (true, rtn.xte)
                }
            }
        }
        return (false, 0.0)
    }

    /**
     * 線に対して直交するか確認
     */
    static func checkCross(
        lastPos: CLLocationCoordinate2D,       //レグ起点
        nextPos: CLLocationCoordinate2D,       //レグ終点
        shipPos: CLLocationCoordinate2D)       //自船位置
    -> Bool
    {
        var checkCross: Bool = false

        let deg1 = bearing(lastPos: lastPos, nextPos: nextPos)   //レグ起点 → レグ終点
        let deg2 = bearing(lastPos: lastPos, nextPos: shipPos)   //レグ起点 → 自船位置
        let deg3 = bearing(lastPos: nextPos, nextPos: shipPos)   //レグ終点 → 自船位置

        //自船を頂点とした方位が９０度以内
        if (deg1.retCode && deg2.retCode && deg3.retCode) {
            let brg1 = abs(deg1.co1 - deg2.co1)
            let brg2 = abs(deg3.co1 - deg1.co2)
            if (brg1 <= 90.0 && brg2 <= 90.0) {
                checkCross = true
            }

            let brg3 = abs(deg1.co2 - deg2.co2)
            let brg4 = abs(deg3.co2 - deg1.co1)
            if (brg3 <= 90.0 && brg4 <= 90.0) {
                checkCross = true
            }
        }
        return checkCross
    }

    /**
     * 線迄の距離
     */
    static func checkPolyline(
        objPos: Array<CLLocationCoordinate2D>,
        shipPos: CLLocationCoordinate2D)
    -> Double?
    {
        var distance: Double?
        let rtn = crossLineDistance(objPos: objPos, shipPos: shipPos)

        //直線と直行していない
        if (!rtn.retCode) {
            let rtn1 = pointsDistance(objPos: objPos, shipPos: shipPos)
            if (rtn1.retCode) {
                distance = nil
            } else {
                distance = rtn1.xte * -1
            }
        } else {
            distance = rtn.xte * -1
        }

        if let dist = distance {
            print(debug: "checkPolyline \(dist)")
        }
        return distance
    }

    /**
     * 線迄の距離
     */
    static func crossLineDistance(
        objPos: Array<CLLocationCoordinate2D>,
        shipPos: CLLocationCoordinate2D)
    -> (retCode: Bool, xte: Double)     //正常終了:true 異常:false 距離[NM]
    {
        //Loopしているか確認
        var isLoop = false
        if objPos[0].latitude == objPos[(objPos.count - 1)].latitude
            && objPos[0].longitude == objPos[(objPos.count - 1)].longitude {
            isLoop = true
        }

        var loopCntMax = objPos.count - 1
        if isLoop {
            loopCntMax = objPos.count - 2
        }

        //自船が直線に直行している場合の距離を求める
        var isOutside = false
        var isCross = false
        var distance = 1000000.0
        for cnt in 0...(loopCntMax - 1) {
            let rtn = eNvXteRL(lastPos: objPos[cnt], nextPos: objPos[(cnt + 1)], shipPos: shipPos)
            if rtn.retCode {
                //自船を頂点とした方位が９０度以内
                if checkCross(lastPos: objPos[cnt], nextPos: objPos[(cnt + 1)], shipPos: shipPos) {
                    if distance > abs(rtn.xte) {
                        isOutside = rtn.xte < 0
                        distance = abs(rtn.xte)
                        isCross = true
                    }
                }

                print(debug: "crossLineDistance \(rtn)/(isOutside)/(distance)")
            }
        }

        if isOutside {
            distance *= -1
        }
        print(debug: "crossLineDistance \(distance)")
        return (isCross, distance)
    }

    /**
     * 点迄の距離
     */
    static func pointsDistance(
        objPos: Array<CLLocationCoordinate2D>,
        shipPos: CLLocationCoordinate2D)
    -> (retCode: Bool, xte: Double)     //正常終了:true 異常:false 距離[NM]
    {
        var distance = 10000000.0
        var nearbyPoint = 0

        //Loopしているか確認
        var isLoop = false
        if objPos[0].latitude == objPos[(objPos.count - 1)].latitude
            && objPos[0].longitude == objPos[(objPos.count - 1)].longitude {
            isLoop = true
        }

        var loopCntMax = objPos.count
        if isLoop {
            loopCntMax = objPos.count - 1
        }

        //一番近いポイントを見つける
        for cnt in 0...(loopCntMax - 1) {
            let newDistance = distance2(lastPos: objPos[cnt], nextPos: shipPos, type: Mrdc.E_NV_MRDC_NORMAL)
            if newDistance.retCode {
                print(debug: "pointsDistance \(newDistance) (cnt)")
                if distance > newDistance.dist {
                    distance = newDistance.dist
                    nearbyPoint = cnt
                }
            }
        }

        //近いポイントの前のポイント
        var beforePoint = 0
        if nearbyPoint == 0 {
            if isLoop {
                beforePoint = loopCntMax - 1
            } else {
                beforePoint = 0
            }
        } else {
            beforePoint = nearbyPoint - 1
        }

        //近いポイントの次のポイント
        var nextPoint = 0
        if nearbyPoint == (loopCntMax - 1) {
            if isLoop {
                nextPoint = 0
            } else {
                nextPoint = nearbyPoint
            }
        } else {
            nextPoint = nearbyPoint + 1
        }

        var outside1 = false
        var distance1 = 0.0
        let rtn1 = eNvXteRL(lastPos: objPos[beforePoint], nextPos: objPos[nearbyPoint], shipPos: shipPos)
        if rtn1.retCode {
            print(debug: "pointsDistance \(rtn1)/(beforePoint)/(nearbyPoint)/(distance)")
            if rtn1.xte < 0 {
                outside1 = true
            }
            distance1 = rtn1.xte
        }

        var outside2 = false
        var distance2 = 0.0
        let rtn2 = eNvXteRL(lastPos: objPos[nearbyPoint], nextPos: objPos[nextPoint], shipPos: shipPos)
        if rtn2.retCode {
            print(debug: "pointsDistance \(rtn2)/(beforePoint)/(nearbyPoint)/(distance)")
            if rtn2.xte < 0 {
                outside2 = true
            }
            distance2 = rtn2.xte
        }

        var outside = false
        if distance1 < distance2 {
            outside = outside1
        } else {
            outside = outside2
        }

        //船が端の点に近く、ラインと直行していない場合はライン判定から外れていることにする
        var lineOut = false
        if nearbyPoint == 0 {
            lineOut = true
        } else if nearbyPoint == (loopCntMax - 1) {
            lineOut = true
        }

        if outside {
            distance *= -1
        }
        print(debug: "pointsDistance \(distance)")
        return (lineOut, distance)
    }

    /**
     * XTE(航路ずれ)計算(RL)
     * 航路データとNextWP番号、自船位置を与えて航路からの距離(XTE)を求める
     *
     * build/src/ENv/ENv.cpp ENvXteRL
     */
    static func eNvXteRL(
        lastPos: CLLocationCoordinate2D,    //レグ起点
        nextPos: CLLocationCoordinate2D,    //レグ終点
        shipPos: CLLocationCoordinate2D)    //自船位置
    -> (retCode: Bool, xte: Double)     //正常終了:true 異常:false 距離[NM]
    {
        let rtn1 = eNvMrdc(lastPos: lastPos, nextPos: nextPos, type: Mrdc.E_NV_MRDC_NORMAL)
        print(debug: "eNvXteRL1 \(rtn1)")
        if (!rtn1.retCode) {
            return (false, 0.0)
        }
        let rRegCo = LocationCalculation.deg2rad(deg: rtn1.co1)
        let rtn2 = eNvMrdc(lastPos: lastPos, nextPos: shipPos, type: Mrdc.E_NV_MRDC_NORMAL)
        print(debug: "eNvXteRL2 \(rtn2)")
        if (!rtn2.retCode) {
            return (false, 0.0)
        }
        let rShipCo = LocationCalculation.deg2rad(deg: rtn2.co1)
        let xte = rtn2.dist * sin(rShipCo - rRegCo)
        return (true, xte)
    }

    /**
     * 等角航法での距離・方位計算
     *
     * build/src/ENv/ENv.cpp ENvMrdc移植
     */
    static func eNvMrdc(
        lastPos: CLLocationCoordinate2D,    //前回位置
        nextPos: CLLocationCoordinate2D,    //次回位置
        type: Mrdc)                         //手法 Normal:E_NV_MRDC_NORMAL forAutoSail:E_NV_MRDC_XTE_R
    -> ShipPosInf
    {
        var bRng1 = 0.0
        var bRng2 = 0.0
        var dist = 0.0
        var ret = ShipPosInf(retCode: false, co1: bRng1, co2: bRng2, dist: dist)

        //前回位置チェック
        if (!eNvCheckPos(checkPos: lastPos)) {
            //位置が地球上にないのでエラー
            ret = ShipPosInf(retCode: false, co1: bRng1, co2: bRng2, dist: dist)
            return ret
        }

        //次回位置チェック
        if (!eNvCheckPos(checkPos: nextPos)) {
            //位置が地球上にないのでエラー
            ret = ShipPosInf(retCode: false, co1: bRng1, co2: bRng2, dist: dist)
            return ret
        }

        //経度座標の符号を合わせる
        var lastLon = lastPos.longitude
        let nextLon = nextPos.longitude
        if (abs(lastLon - nextLon) > 180.0) {
            if (lastLon < 0.0) {
                lastLon += 360.0
            } else {
                lastLon -= 360.0
            }
        }

        //同じ位置かチェック
        //『前回位置』と『次回位置』の差が0.00001'以下なら同じ位置とみなす
        if ((abs(lastPos.latitude - nextPos.latitude) <= errorRange) &&
            (abs(lastLon - nextLon) <= errorRange)) {
            ret = ShipPosInf(retCode: false, co1: 0.0, co2: 180.0, dist: 0.0)
            return ret
        }

        //度単位のデータをラジアンに変換
        let rLat0 = LocationCalculation.deg2rad(deg: lastPos.latitude)
        let rLon0 = LocationCalculation.deg2rad(deg: lastPos.longitude)
        let rLat1 = LocationCalculation.deg2rad(deg: nextPos.latitude)
        let rLon1 = LocationCalculation.deg2rad(deg: nextPos.longitude)

        //変緯・変経を求める
        let aTanA = eNvConvRad(longiture: rLon1 - rLon0)    //変経計算
        var work2 = eNvMrLat(latitude: nextPos.latitude)    //漸長緯度に変換
        var work1 = eNvMrLat(latitude: lastPos.latitude)    //漸長緯度に変換
        let aTanB = LocationCalculation.deg2rad(deg: work2 - work1) //変緯計算
        //前回位置から次回位置への方位を求める
        if ((aTanA != 0.0) || (aTanB != 0.0)) {
            work1 = atan2(aTanA, aTanB)
            bRng1 = work1
        }

        var bCalc = false
        switch type {
        case Mrdc.E_NV_MRDC_XTE_R:
            if (abs(rLat1 - rLat0) > 0.0000001) {
                bCalc = true
            }
        default:
            if (abs((abs(bRng1) - pidDiv2)) >= (Double.pi / 180.0)) {
                bCalc = true
            }
        }

        //2点間の距離を求める
        var approximation = false
        if (bCalc) {
            //漸長緯度航法で距離を求める
            work2 = cos(work1)
            dist = (rLat1 - rLat0) / work2
        } else {
            //針路が90度または270度の場合
            //中分緯度航法で距離を求める
            dist = sqrt((rLat1 - rLat0) * (rLat1 - rLat0) + cos(rLat0) * cos(rLat0) *
                        eNvConvRad(longiture: rLon1 - rLon0) * eNvConvRad(longiture: rLon1 - rLon0))
            approximation = true
            //手法によってずれ量を変える
            var dist01: Double
            switch type {
            case Mrdc.E_NV_MRDC_XTE_R:
                dist01 = 0.00000011
            default:
                //0.11度ずれた緯度の質を求める
                dist01 = (tan(LocationCalculation.deg2rad(deg: 0.11)) * abs(LocationCalculation.rad2deg(rad: dist) * deg2NM)) / 60.0
            }
            let nextPosTmp = CLLocationCoordinate2D(latitude: (nextPos.latitude + dist01), longitude: nextPos.longitude)
            let shipInf = eNvMrdc(lastPos: lastPos, nextPos: nextPosTmp, type: type)
            if (!shipInf.retCode) {
                ret = ShipPosInf(retCode: false, co1: 0.0, co2: 180.0, dist: 0.0)
                return ret
            }
            dist = shipInf.dist
        }
        bRng1 = eNvConvDeg(dDeg: LocationCalculation.rad2deg(rad: bRng1))
        bRng2 = eNvConvDeg(dDeg: bRng1 + 180.0)
        if (!approximation) {
            dist = LocationCalculation.rad2deg(rad: dist) * deg2NM
        }
        dist = abs(dist)
        return ShipPosInf(retCode: true, co1: bRng1, co2: bRng2, dist: dist)
    }

    /**
     * 等角航法での距離
     *
     * build/src/ENv/ENv.cpp ENvMrdc移植
     */
    static func distance2(
        lastPos: CLLocationCoordinate2D,    //前回位置
        nextPos: CLLocationCoordinate2D,    //次回位置
        type: Mrdc)                         //手法 Normal:E_NV_MRDC_NORMAL forAutoSail:E_NV_MRDC_XTE_R
    -> DistanceInf
    {
        var bRng1 = 0.0
        var dist = 0.0
        var ret = DistanceInf(retCode: false, dist: dist)

        //前回位置チェック
        if (!eNvCheckPos(checkPos: lastPos)) {
            //位置が地球上にないのでエラー
            ret = DistanceInf(retCode: false, dist: dist)
            return ret
        }

        //次回位置チェック
        if (!eNvCheckPos(checkPos: nextPos)) {
            //位置が地球上にないのでエラー
            ret = DistanceInf(retCode: false, dist: dist)
            return ret
        }

        //経度座標の符号を合わせる
        var lastLon = lastPos.longitude
        let nextLon = nextPos.longitude
        if (abs(lastLon - nextLon) > 180.0) {
            if (lastLon < 0.0) {
                lastLon += 360
            } else {
                lastLon -= 360
            }
        }

        //同じ位置かチェック
        //『前回位置』と『次回位置』の差が0.00001'以下なら同じ位置とみなす
        if ((abs(lastPos.latitude - nextPos.latitude) <= errorRange) &&
            (abs(lastLon - nextLon) <= errorRange)) {
            ret = DistanceInf(retCode: false,  dist: dist)
            return ret
        }

        //度単位のデータをラジアンに変換
        let rLat0 = LocationCalculation.deg2rad(deg: lastPos.latitude)
        let rLon0 = LocationCalculation.deg2rad(deg: lastPos.longitude)
        let rLat1 = LocationCalculation.deg2rad(deg: nextPos.latitude)
        let rLon1 = LocationCalculation.deg2rad(deg: nextPos.longitude)

        //変緯・変経を求める
        let aTanA = eNvConvRad(longiture: rLon1 - rLon0)    //変経計算
        var work2 = eNvMrLat(latitude: nextPos.latitude)    //漸長緯度に変換
        var work1 = eNvMrLat(latitude: lastPos.latitude)    //漸長緯度に変換
        let aTanB = LocationCalculation.deg2rad(deg: work2 - work1) //変緯計算
        //前回位置から次回位置への方位を求める
        if ((aTanA != 0.0) || (aTanB != 0.0)) {
            work1 = atan2(aTanA, aTanB)
            bRng1 = work1
        }

        var bCalc = false
        switch type {
        case Mrdc.E_NV_MRDC_XTE_R:
            if (abs(rLat1 - rLat0) > 0.0000001) {
                bCalc = true
            }
        default:
            if (abs((abs(bRng1) - pidDiv2)) >= (Double.pi / 180.0)) {
                bCalc = true
            }
        }

        //2点間の距離を求める
        var approximation = false
        if (bCalc) {
            //漸長緯度航法で距離を求める
            work2 = cos(work1)
            dist = (rLat1 - rLat0) / work2
        } else {
            //針路が90度または270度の場合
            //中分緯度航法で距離を求める
            dist = sqrt((rLat1 - rLat0) * (rLat1 - rLat0) + cos(rLat0) * cos(rLat0) *
                        eNvConvRad(longiture: rLon1 - rLon0) * eNvConvRad(longiture: rLon1 - rLon0))
            approximation = true
            //手法によってずれ量を変える
            var dist01: Double
            switch type {
            case Mrdc.E_NV_MRDC_XTE_R:
                dist01 = 0.00000011
            default:
                //0.11度ずれた緯度の質を求める
                dist01 = (tan(LocationCalculation.deg2rad(deg: 0.11)) * abs(LocationCalculation.rad2deg(rad: dist) * deg2NM)) / 60.0
            }
            let nextPosTmp = CLLocationCoordinate2D(latitude: (nextPos.latitude + dist01), longitude: nextPos.longitude)
            let shipInf = eNvMrdc(lastPos: lastPos, nextPos: nextPosTmp, type: type)
            if (!shipInf.retCode) {
                ret = DistanceInf(retCode: false, dist: 0.0)
                return ret
            }
            dist = shipInf.dist
        }
        if (!approximation) {
            dist = LocationCalculation.rad2deg(rad: dist) * deg2NM
        }
        dist = abs(dist)
        print( debug: "dist:\(dist)" )
        return DistanceInf(retCode: true, dist: dist)
    }

    /**
     * 方位計算
     *
     * build/src/ENv/ENv.cpp ENvMrdc移植
     */
    static func bearing(
        lastPos: CLLocationCoordinate2D,       //前回位置
        nextPos: CLLocationCoordinate2D)       //次回位置
    -> BearingInf
    {
        var bRng1 = 0.0
        var bRng2 = 0.0
        var ret = BearingInf(retCode: false, co1: bRng1, co2: bRng2)

        //前回位置チェック
        if (!eNvCheckPos(checkPos: lastPos)) {
            //位置が地球上にないのでエラー
            let ret = BearingInf(retCode: false, co1: 0.0, co2: 0.0)
            return ret
        }
        //次回位置チェック
        if (!eNvCheckPos(checkPos: nextPos)) {
            //位置が地球上にないのでエラー
            let ret = BearingInf(retCode: false, co1: 0.0, co2: 0.0)
            return ret
        }

        //経度座標の符号を合わせる
        var lastLon = lastPos.longitude
        let nextLon = nextPos.longitude
        if (abs(lastLon - nextLon) > 180.0) {
            if (lastLon < 0.0) {
                lastLon += 360
            } else {
                lastLon -= 360
            }
        }

        //同じ位置かチェック
        //『前回位置』と『次回位置』の差が0.00001'以下なら同じ位置とみなす
        if ((abs(lastPos.latitude - nextPos.latitude) <= errorRange) &&
            (abs(lastLon - nextLon) <= errorRange)) {
            ret = BearingInf(retCode: false, co1: 0.0, co2: 180.0)
            return ret
        }

        //度単位のデータをラジアンに変換
        let rLon0 = LocationCalculation.deg2rad(deg: lastPos.longitude)
        let rLon1 = LocationCalculation.deg2rad(deg: nextPos.longitude)

        //変緯・変経を求める
        let aTanA = eNvConvRad(longiture: rLon1 - rLon0)    //変経計算
        let work2 = eNvMrLat(latitude: nextPos.latitude)    //漸長緯度に変換
        let work1 = eNvMrLat(latitude: lastPos.latitude)    //漸長緯度に変換
        let aTanB = deg2rad(deg: work2 - work1) //変緯計算

        //前回位置から次回位置への方位を求める
        if ((aTanA != 0.0) || (aTanB != 0.0)) {
            bRng1 = atan2(aTanA, aTanB)
        } else {
            bRng1 = 0.0
        }

        bRng1 = eNvConvDeg(dDeg: LocationCalculation.rad2deg(rad: bRng1))
        bRng2 = eNvConvDeg(dDeg: bRng1 + 180.0)

        return BearingInf(retCode: true, co1: bRng1, co2: bRng2)
    }

    /**
     * 位置データチェック
     */
    static func eNvCheckPos(
        checkPos: CLLocationCoordinate2D) -> Bool
    {
        var check = false
        if (checkPos.latitude <= 90.0 &&
            checkPos.latitude >= -90.0 &&
            checkPos.longitude <= 180.0 &&
            checkPos.longitude >= -180.0) {
            check = true
        }
        return check
    }

    /**
     * ラジアンデータ変換
     * 引数が2πより大きいか-2πより小さいとき、2π〜-2πになるように丸める
     */
    static func eNvConvRad(longiture: Double) -> Double {
        var lon: Double
        if (longiture <= -Double.pi) {
            lon = longiture + Double.pi * 2.0
        } else if (longiture > Double.pi) {
            lon = longiture - Double.pi * 2.0
        } else {
            lon = longiture
        }
        return lon
    }

    /**
     * 漸長緯度計算
     * 指定緯度の漸長緯度を求める
     */
    static func eNvMrLat(latitude: Double) -> Double {   // ENvMrLat
        let rLat = abs(latitude * Double.pi / 180)
        let work1 = log(tan(rad45 + rLat / 2.0))
        let work2 = pow(eEccentricity, 2.0) * sin(rLat)
        let work3 = pow(eEccentricity, 4.0) * pow(sin(rLat), 3.0) / 3.0
        let mrLat = work1 - (work2 + work3)

        var lat: Double
        if (latitude < 0.0) {
            lat = mrLat * -1 * rDeg
        } else {
            lat = abs(mrLat) * rDeg
        }
        return lat
    }

    /**
     * 度データ変換
     * ±の度データを+0〜360度のデータに変換する
     *
     * build/src/ENv/ENv.cpp ENvConvDeg
     */
    static func eNvConvDeg(
        dDeg: Double
    ) -> Double {
        var deg = dDeg

        if (deg >= 360.0) {
            //360度以上なら360度を減算
            deg -= 360.0
        }

        if (deg < 0.0) {
            //0度より小さいなら360度を加算
            deg += 360.0
        }

        if (deg == 360.0) {
            deg = 0.0
        }

        return deg
    }

    /**
     * ポイントがライン上にあるか
     *
     * build/src/EDrawChart/ECdgeometry.cpp ECdIsPointOnLine移植
     */
    static func eCdIsPointOnLine(
        pt: CLLocationCoordinate2D,
        ptL: CLLocationCoordinate2D,
        ptR: CLLocationCoordinate2D
    ) -> Bool {
        var checkN = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        checkN.latitude = ptL.latitude - pt.latitude
        checkN.longitude = ptL.longitude - pt.longitude
        if checkN.latitude == 0 && checkN.longitude == 0 {
            return true
        }

        var zero = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        zero.latitude = ptR.latitude - pt.latitude
        zero.longitude = ptR.longitude - pt.longitude
        if zero.latitude == 0 && zero.longitude == 0 {
            return true
        }

        let inside = zero.latitude * checkN.latitude + zero.longitude * checkN.longitude
        let outside = zero.latitude * checkN.longitude - checkN.latitude * zero.longitude
        if fabs(outside) <= ACCY_ROOT {
            if fabs(inside) <= ACCY_ROOT {
                return true // 内積、外積が０ということは、点がポリゴンの頂点のどれかと一致しているということ
            }
            if inside < 0.0 {
                return true // 内積が０ということは、点がポリゴンの線上にあるということ
            }
        }
        return false
    }

    /**
     * 自己交差チェック(32ビット正規化座標用)
     *
     * build/src/Chart/Stranding/EWc_Inner.cpp移植
     */
    static func chkSelfIntersection(
        wcPos: Array<CLLocationCoordinate2D>
    ) -> Bool {
        let positionNum = wcPos.count
        var point1: Array<CLLocationCoordinate2D> = [CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0), CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)]
        var point2: Array<CLLocationCoordinate2D> = [CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0), CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)]
        var idx1: Array<Int> = [0, 0]
        var idx2: Array<Int> = [0, 0]
        var bIntersect = false

        for i in 0 ..< positionNum - 1 {
            point1[0] = wcPos[i]
            point1[1] = wcPos[i + 1]

            if point1[0] == point1[1] {
                continue
            }

            idx1[0] = i
            idx1[1] = i + 1
            for j in i + 1 ..< positionNum - 1 {
                bIntersect = false
                point2[0] = wcPos[j]
                point2[1] = wcPos[j + 1]

                if point2[0] == point2[1] {
                    continue
                }

                idx2[0] = j
                idx2[1] = j + 1

                if min(point1[0].longitude, point1[1].longitude) > max(point2[0].longitude, point2[1].longitude) {
                    continue
                }
                if max(point1[0].longitude, point1[1].longitude) < min(point2[0].longitude, point2[1].longitude) {
                    continue
                }
                if min(point1[0].latitude, point1[1].latitude) > max(point2[0].latitude, point2[1].latitude) {
                    continue
                }
                if max(point1[0].latitude, point1[1].latitude) < min(point2[0].latitude, point2[1].latitude) {
                    continue
                }

                let ret = eCdInterPointLL(pSp: point1, pEp: point2)
                if ret.crossInf == 0 {
                    bIntersect = true    // 交点あり
                } else if ret.crossInf == 1 {
                    // 接線（頂点と頂点の接触、辺と頂点の接触）
                    if !(point1[1] == point2[0]) && (i + 1 == j) {
                        if !(i == 0 && ((j + 1) == (positionNum - 1)) && (point1[0] == point2[1])) {
                            bIntersect = true
                        }
                    }
                } else if ret.crossInf == -2 {
                    // 平行
                    if point1[0].longitude != point1[1].longitude && point2[0].longitude != point2[1].longitude {
                        // 線分2の座標が線分1上に有るか調べる
                        var targetPt: CLLocationCoordinate2D
                        var ptR: CLLocationCoordinate2D
                        var ptL: CLLocationCoordinate2D
                        let minX = min(point1[0].longitude, point1[1].longitude)
                        let maxX = max(point1[0].longitude, point1[1].longitude)

                        if minX < point2[0].longitude && maxX > point2[0].longitude {
                            targetPt = point2[0]
                        } else {
                            targetPt = point2[1]
                        }

                        if point1[0].longitude < point1[1].longitude {
                            ptL = point1[0]
                            ptR = point1[1]
                        } else {
                            ptL = point1[1]
                            ptR = point1[0]
                        }

                        let ret = eCdIsPointOnLine(pt: targetPt, ptL: ptL, ptR: ptR)
                        if true == ret {
                            if (min(point1[0].longitude, point1[1].longitude) <= min(point2[0].longitude, point2[1].longitude) && max(point2[0].longitude, point2[1].longitude) <= max(point1[0].longitude, point1[1].longitude))
                                || (min(point2[0].longitude, point2[1].longitude) <= min(point1[0].longitude, point1[1].longitude) && max(point1[0].longitude, point1[1].longitude) <= max(point2[0].longitude, point2[1].longitude)) {
                                bIntersect = true
                            } else if !((point1[1] == point2[0]) && (i + 1 == j)) {
                                if !((i == 0) && ((j + 1) == (positionNum - 1)) && (point1[0] == point2[1])) {
                                    bIntersect = true
                                }
                            }
                        }
                    } else if point1[0].longitude == point2[0].longitude {
                        // 縦一線の場合
                        if point1[1].latitude == point2[1].latitude {
                            bIntersect = true
                        } else if (min(point1[0].latitude, point1[1].latitude) < point2[1].latitude && max(point1[0].latitude, point1[1].latitude) > point2[1].latitude)
                                    && (min(point2[0].latitude, point2[1].latitude) < point1[1].latitude && max(point2[0].latitude, point2[1].latitude) > point1[1].latitude) {
                            bIntersect = true
                        } else if (min(point1[0].latitude, point1[1].latitude) < point2[0].latitude && max(point1[0].latitude, point1[1].latitude) > point2[0].latitude)
                                    && (min(point2[0].latitude, point2[1].latitude) < point1[0].latitude && max(point2[0].latitude, point2[1].latitude) > point1[0].latitude) {
                            bIntersect = true
                        } else if min(point1[0].latitude, point1[1].latitude) < point2[1].latitude && max(point1[0].latitude, point1[1].latitude) > point2[1].latitude {
                            bIntersect = true
                        } else if min(point2[0].latitude, point2[1].latitude) < point1[1].latitude && max(point2[0].latitude, point2[1].latitude) > point1[1].latitude {
                            bIntersect = true
                        }
                    }
                }
                // 自己交差している
                if true == bIntersect {
                    return true
                }
            }
        }
        return false
    }

   /**
    * 2線分の交点を計算
    * 0:交点有り 1:接線 -1:線分が存在しない
    * -2:二線分が並行し交点がない
    * -3:線分外で交点あり
    *
    * src/EDrawChart/ECdGeometry.cpp
    */
    static func eCdInterPointLL(
        pSp: Array<CLLocationCoordinate2D>,
        pEp: Array<CLLocationCoordinate2D>
    ) -> (crossInf: Int, cross: CLLocationCoordinate2D) {
        var resp: Int = 0
        var sd = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        var dd = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        var cross = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)

        sd.longitude = pSp[1].longitude - pSp[0].longitude
        sd.latitude = pSp[1].latitude - pSp[0].latitude
        dd.longitude = pEp[1].longitude - pEp[0].longitude
        dd.latitude = pEp[1].latitude - pEp[0].latitude
        let rsqs = sd.longitude * sd.longitude + sd.latitude * sd.latitude
        let rsqe = dd.longitude * dd.longitude + dd.latitude * dd.latitude

        //引数の適切性をチェック
        if (rsqe < ACCY_ROOT) || (rsqs < ACCY_ROOT) {
            //引数が不適切のため、計算中止
            resp = -1
        } else {
            // ２線分の始点差ベクトルを求める
            var bd = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
            bd.longitude = pEp[0].longitude - pSp[0].longitude
            bd.latitude = pEp[0].latitude - pSp[0].latitude

            // ２線分の外積が０に近い時は平行となる
            let det = dd.longitude * sd.latitude - dd.latitude * sd.longitude
            if fabs(det) < ACCY_ROOT {
                resp = -2;
            } else {
                // 始点差ベクトルとの外積から（外積比によって）パラメータを計算
                let detinv = 1.0 / det
                let s = (dd.longitude * bd.latitude - dd.latitude * bd.longitude) * detinv
                let t = (sd.longitude * bd.latitude - sd.latitude * bd.longitude) * detinv
                let ns = s
                let nt = t
                // パラメータから交点を計算
                var l = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
                l.longitude = pSp[0].longitude + sd.longitude * s
                l.latitude = pSp[0].latitude + sd.latitude * s

                cross = l

                // 交点が２線分の外にある？
                if (ns + ACCY_ROOT < 0.0) || (ns - ACCY_ROOT > 1.0) || (nt + ACCY_ROOT < 0.0) || (nt - ACCY_ROOT > 1.0) {
                    resp = -3;
                } else if (fabs(ns) <= ACCY_ROOT) || (fabs(ns - 1.0) <= ACCY_ROOT) || (fabs(nt) <= ACCY_ROOT) || (fabs(nt - 1.0) <= ACCY_ROOT) {
                    resp = 1

                    // 接線判定になっても算出された交点が
                    // ・線分がなす矩形に内包されていない場合は線分外とする
                    // ・各線分の始点と終点に合致しない場合は交点ありとする
                    repeat {
                        var lSp: Array<PointC> = [PointC(x: 0, y:0), PointC(x: 0, y:0)]
                        var lEp: Array<PointC> = [PointC(x: 0, y:0), PointC(x: 0, y:0)]
                        var ptCross = PointC(x: 0, y: 0)

                        lSp[0].x = Int(pSp[0].longitude)
                        lSp[0].y = Int(pSp[0].latitude)
                        lSp[1].x = Int(pSp[1].longitude)
                        lSp[1].y = Int(pSp[1].latitude)
                        lEp[0].x = Int(pEp[0].longitude)
                        lEp[0].y = Int(pEp[0].latitude)
                        lEp[1].x = Int(pEp[1].longitude)
                        lEp[1].y = Int(pEp[1].latitude)

                        ptCross.x = (Int)((cross.longitude < 0) ? (cross.longitude - 0.5) : (cross.longitude + 0.5))
                        ptCross.y = (Int)((cross.latitude < 0) ? (cross.latitude - 0.5) : (cross.latitude + 0.5))

                        // 線分がなす矩形に内包されていない場合は線分外とする
                        if lSp[0].x < lSp[1].x {
                            if ptCross.x < lSp[0].x || lSp[1].x < ptCross.x {
                                resp = -3
                                break
                            }
                        } else {
                            if ptCross.x < lSp[1].x || lSp[0].x < ptCross.x {
                                resp = -3
                                break
                            }
                        }

                        if lEp[0].x < lEp[1].x {
                            if ptCross.x < lEp[0].x || lEp[1].x < ptCross.x {
                                resp = -3
                                break
                            }
                        } else {
                            if ptCross.x < lEp[1].x || lEp[0].x < ptCross.x {
                                resp = -3
                                break
                            }
                        }

                        if lSp[0].y < lSp[1].y {
                            if ptCross.y < lSp[0].y || lSp[1].y < ptCross.y {
                                resp = -3
                                break
                            }
                        } else {
                            if ptCross.y < lSp[1].y || lSp[0].y < ptCross.y {
                                resp = -3
                                break
                            }
                        }

                        if lEp[0].y < lEp[1].y {
                            if ptCross.y < lEp[0].y || lEp[1].y < ptCross.y {
                                resp = -3
                                break
                            }
                        } else {
                            if ptCross.y < lEp[1].y || lEp[0].y < ptCross.y {
                                resp = -3
                                break
                            }
                        }

                        // 各線分の始点と終点に合致しない場合は交点ありとする
                        if !((lSp[0].x == ptCross.x) && (lSp[0].y == ptCross.y)) &&
                           !((lSp[1].x == ptCross.x) && (lSp[1].y == ptCross.y)) &&
                           !((lEp[0].x == ptCross.x) && (lEp[0].y == ptCross.y)) &&
                           !((lEp[1].x == ptCross.x) && (lEp[1].y == ptCross.y)) {
                            resp = 0
                            break
                        }
                    } while false
                } else {
                    resp = 0

                    // 交差判定になっても算出された交点が
                    // 線分がなす矩形に内包されていない場合は線分外とする
                    repeat {
                        var lSp: Array<PointC> = [PointC(x: 0, y:0), PointC(x: 0, y:0)]
                        var lEp: Array<PointC> = [PointC(x: 0, y:0), PointC(x: 0, y:0)]
                        var ptCross = PointC(x: 0, y: 0)

                        lSp[0].x = Int(pSp[0].longitude)
                        lSp[0].y = Int(pSp[0].latitude)
                        lSp[1].x = Int(pSp[1].longitude)
                        lSp[1].y = Int(pSp[1].latitude)
                        lEp[0].x = Int(pEp[0].longitude)
                        lEp[0].y = Int(pEp[0].latitude)
                        lEp[1].x = Int(pEp[1].longitude)
                        lEp[1].y = Int(pEp[1].latitude)

                        ptCross.x = (Int)((cross.longitude < 0) ? (cross.longitude - 0.5) : (cross.longitude + 0.5))
                        ptCross.y = (Int)((cross.latitude < 0) ? (cross.latitude - 0.5) : (cross.latitude + 0.5))

                        if lSp[0].x < lSp[1].x {
                            if ptCross.x < lSp[0].x || lSp[1].x < ptCross.x {
                                resp = -3
                                break
                            }
                        } else {
                            if ptCross.x < lSp[1].x || lSp[0].x < ptCross.x {
                                resp = -3
                                break
                            }
                        }

                        if lEp[0].x < lEp[1].x {
                            if ptCross.x < lEp[0].x || lEp[1].x < ptCross.x {
                                resp = -3
                                break
                            }
                        } else {
                            if ptCross.x < lEp[1].x || lEp[0].x < ptCross.x {
                                resp = -3
                                break
                            }
                        }

                        if lSp[0].y < lSp[1].y {
                            if ptCross.y < lSp[0].y || lSp[1].y < ptCross.y {
                                resp = -3
                                break
                            }
                        } else {
                            if ptCross.y < lSp[1].y || lSp[0].y < ptCross.y {
                                resp = -3
                                break
                            }
                        }

                        if lEp[0].y < lEp[1].y {
                            if ptCross.y < lEp[0].y || lEp[1].y < ptCross.y {
                                resp = -3
                                break
                            }
                        } else {
                            if ptCross.y < lEp[1].y || lEp[0].y < ptCross.y {
                                resp = -3
                                break
                            }
                        }

                        // 各線分の始点と終点に合致しない場合は交点ありとする
                        if ((lSp[0].x == ptCross.x) && (lSp[0].y == ptCross.y)) ||
                           ((lSp[1].x == ptCross.x) && (lSp[1].y == ptCross.y)) ||
                           ((lEp[0].x == ptCross.x) && (lEp[0].y == ptCross.y)) ||
                           ((lEp[1].x == ptCross.x) && (lEp[1].y == ptCross.y)) {
                            resp = 1
                            break
                        }
                    } while false
                }
            }
        }
        return (resp, cross)
    }

    /**
     * 等角航法での推測位置計算
     * 等角航法で始点座標とその方位から任意の距離における位置座標を求める
     *
     * build/src/ENv/ENv.cpp ENvMrnp移植
     */
    static func eNvMrnp(
        startPos: CLLocationCoordinate2D,
        course: Double,
        distance: Double
    ) -> CLLocationCoordinate2D { // ENvMrnp
        var drPos = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        if (!LocationCalculation.checkPosition(pos: startPos)) {
            return drPos
        }

        let rangeCourse = LocationCalculation.rangeDegree(degree: course)
        drPos.latitude = startPos.latitude + distance * cos(rangeCourse * Double.pi / 180.0) / 60.0
        if ((abs(rangeCourse - 90.0) >= (0.1 + brg)) && (abs(rangeCourse - 270.0) >= (0.1 + brg))) {
            drPos.longitude = startPos.longitude + (eNvMrLat(latitude: drPos.latitude) - eNvMrLat(latitude: startPos.latitude)) * tan(rangeCourse * Double.pi / 180.0)
        } else {
            let courseEx: Double
            if (abs(rangeCourse - 270.0) >= (0.1 + brg)) {
                courseEx = 90.11
            } else {
                courseEx = 270.11
            }

            let drPosEx = eNvMrnp(startPos: startPos, course: courseEx, distance: distance)
            drPos.longitude = drPosEx.longitude
        }

        if (180.0 < drPos.longitude) {
            drPos.longitude -= 360.0
        }
        if (drPos.longitude < -180.0) {
            drPos.longitude += 360.0
        }

        return drPos
    }

    /**
     * 直線と直線の交点検索
     *
     * build/src/Chart/DecisionAlgorithm.cpp
     * テストまだ
     */
    static func checkCrossLine(
        p1: CLLocationCoordinate2D,
        p2: CLLocationCoordinate2D,
        sp: CLLocationCoordinate2D,
        ep: CLLocationCoordinate2D
    ) -> Int {
        var Max = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        var Min = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        var crossp = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
        //直線p1-p2(Y=AX+B)とsp-ep(Y=CX+D)の交点チェック
        //同じ経度の垂直線
        if ((p2.longitude == p1.longitude) && (p1.longitude == ep.longitude) && (ep.longitude == sp.longitude)) {
            Max.latitude = p1.latitude < p2.latitude ? p1.latitude : p2.latitude
            Min.latitude = p1.latitude > p2.latitude ? p1.latitude : p2.latitude
            //開始点がp1-p2に含まれる場合
            if ((sp.latitude >= Min.latitude) && (sp.latitude <= Max.latitude)) {
                crossp.latitude = sp.latitude
                crossp.longitude = sp.longitude
                return 1
            }
            //終了点がp1-p2に含まれる場合
            else if (ep.latitude >= Min.latitude) && (ep.latitude <= Max.latitude) {
                crossp.latitude = ep.latitude
                crossp.longitude = ep.longitude
                return 1
            }

            Max.latitude = sp.latitude < ep.latitude ? sp.latitude : ep.latitude
            Min.latitude = sp.latitude > ep.latitude ? sp.latitude : ep.latitude
            //p1がsp-epに含まれる場合
            if ((p1.latitude >= Min.latitude) && (p1.latitude <= Max.latitude)) {
                crossp.latitude = p1.latitude
                crossp.longitude = p1.longitude
                return 1
            }
            //p2がsp-epに含まれる場合
            if ((p2.latitude >= Min.latitude) && (p2.latitude <= Max.latitude)) {
                crossp.latitude = p2.latitude
                crossp.longitude = p2.longitude
                return 1
            }
            return 0
        }

        //同じ緯度の水平線
        if ((p2.latitude == p1.latitude) && (p1.latitude == ep.latitude) && (ep.latitude == sp.latitude)) {
            Max.longitude = p1.longitude < p2.longitude ? p1.longitude : p2.longitude
            Min.longitude = p1.longitude > p2.longitude ? p1.longitude : p2.longitude
            //開始点がp1-p2に含まれる場合
            if ((sp.longitude >= Min.longitude) && sp.longitude <= Max.longitude) {
                crossp.latitude = sp.latitude
                crossp.longitude = sp.longitude
                return 1
            }
            //終了点がp1-p2に含まれる場合
            if ((ep.longitude >= Min.longitude) && (ep.longitude <= Max.longitude)) {
                crossp.latitude = ep.latitude
                crossp.longitude = ep.longitude
                return 1
            }

            Max.longitude = sp.longitude < ep.longitude ? sp.longitude : ep.longitude
            Min.longitude = sp.longitude > ep.longitude ? sp.longitude : ep.longitude
            //p1がsp-epに含まれる場合
            if ((p1.longitude >= Min.longitude) && (p1.longitude <= Max.longitude)) {
                crossp.latitude = p1.latitude
                crossp.longitude = p1.longitude
                return 1
            }
            //p2がwp-epに含まれる場合
            if ((p2.longitude >= Min.longitude) && (p2.longitude <= Max.longitude)) {
                crossp.latitude = p2.latitude
                crossp.longitude = p2.longitude
                return 1
            }
            return 0
        }

        //違う垂直線
        if ((p2.longitude == p1.longitude) && (ep.longitude == sp.longitude)) {
            return 0
        }
        //違う水平線
        if ((p2.latitude == p1.latitude) && (ep.latitude == sp.latitude)) {
            return 0
        }

        var a, b, c, d : Double
        crossp.longitude = 400
        if (p2.longitude == p1.longitude) {
            //p1-p2だけ垂直線
            crossp.longitude = p1.longitude
            crossp.latitude = (ep.latitude - sp.latitude) / (ep.longitude - sp.longitude) * p1.longitude + (sp.latitude * ep.longitude - sp.longitude * ep.latitude) / (ep.longitude - sp.longitude)
        } else if (ep.longitude == sp.longitude) {
            //sp-epだけ垂直線
            crossp.longitude = sp.longitude
            crossp.latitude = (p2.latitude - p1.latitude) / (p2.longitude - p1.longitude) * sp.longitude + (p1.latitude * p2.longitude - p1.longitude * p2.latitude) / (p2.longitude - p1.longitude)
        } else if (p2.latitude == p1.latitude) {
            //p1-p2だけ水平線
            crossp.latitude = p1.latitude
            crossp.longitude = (p1.latitude * (ep.longitude - sp.longitude) - (sp.latitude * ep.longitude - sp.longitude * ep.latitude)) / (ep.latitude - sp.latitude)
        } else if (sp.latitude == ep.latitude) {
            //sp-epだけ水平線
            crossp.latitude = sp.latitude
            crossp.longitude = (sp.latitude * (p2.longitude - p1.longitude) - (p1.latitude * p2.longitude - p1.longitude * p2.latitude)) / (p2.latitude - p1.latitude)
        } else if ((p2.latitude - p1.latitude) / (p2.longitude - p1.longitude) == (ep.latitude - sp.latitude) / (ep.longitude - sp.longitude)) {
            //両方とも垂直線、平行線ではなく２本の線が平行である時
            crossp.longitude = p1.longitude
            crossp.latitude = (ep.latitude - sp.latitude) / (ep.longitude - sp.longitude) * p1.longitude + (sp.latitude * ep.longitude - sp.longitude * ep.latitude) / (ep.longitude - sp.longitude)
            //p1がsp-ep上の点か?
            if (crossp.latitude == p1.latitude) {
                return 1
            }
            crossp.longitude = p2.longitude
            crossp.latitude = (ep.latitude - sp.latitude) / (ep.longitude - sp.longitude) * p2.longitude + (sp.latitude * ep.longitude - sp.longitude * ep.latitude) / (ep.longitude - sp.longitude)
            //p2がsp-ep上の点か?
            if (crossp.latitude == p2.latitude) {
                return 1
            }
            crossp.longitude = sp.longitude
            crossp.latitude = (p2.latitude - p1.latitude) / (p2.longitude - p1.longitude) * sp.longitude + (p1.latitude * p2.longitude - p1.longitude * p2.latitude) / (p2.longitude - p1.longitude)
            //spがp1-p2上の点か?
            if (crossp.latitude == sp.latitude) {
                return 1
            }
            crossp.longitude = ep.longitude
            crossp.latitude = (p2.latitude - p1.latitude) / (p2.longitude - p1.longitude) * ep.longitude + (p1.latitude * p2.longitude - p1.longitude * p2.latitude) / (p2.longitude - p1.longitude)
            //epがp1-p2上の点か?
            if (crossp.latitude == ep.latitude) {
                return 1
            }
            //それ以外--別の平行線
            return 0
        } else {
            a = (p2.latitude - p1.latitude) / (p2.longitude - p1.longitude)
            b = (p1.latitude * p2.longitude - p1.longitude * p2.latitude) / (p2.longitude - p1.longitude)
            c = (ep.latitude - sp.latitude) / (ep.longitude - sp.longitude)
            d = (sp.latitude * ep.longitude - sp.longitude * ep.latitude) / (ep.longitude - sp.longitude)
            crossp.longitude = (d - b) / (a - c)
            crossp.latitude = (c * b - a * d) / (c - a)
        }

        //@@@ 2000/10/13 交点がどの線分上に存在するか判定する部分は、交点そのものをDOUBLEで計算していることから
        //            その線分の性質に沿った判定をする必要がある。
        //            例えば水平線と任意の傾きをもった線分が交差する場合その交点を計算式で求めてもY座標値は
        //            水平線のY座標とは異なってしまうはず（論理的には正しくとも、倍精度計算の丸め誤差により
        //              正確に一致はしない）よって計算結果の有効桁数を小数点以下６桁で計算する。
        if (crossp.longitude != 400) {
            var iMaxX, iMaxY, iMinX, iMinY, iCrosX, iCrosY: Int

            //求めた交点が線分p1-p2, sp-ep上にあるか？
            Min.longitude = p1.longitude < p2.longitude ? p1.longitude : p2.longitude
            Max.longitude = p1.longitude > p2.longitude ? p1.longitude : p2.longitude
            Min.latitude = p1.latitude < p2.latitude ? p1.latitude : p2.latitude
            Max.latitude = p1.latitude > p2.latitude ? p1.latitude : p2.latitude
            iMinX = Int(round(Min.longitude * DI_BIAS))
            iMinY = Int(round(Min.latitude * DI_BIAS))
            iMaxX = Int(round(Max.longitude * DI_BIAS))
            iMaxY = Int(round(Max.latitude * DI_BIAS))
            iCrosX = Int(round(crossp.longitude * DI_BIAS))
            iCrosY = Int(round(crossp.latitude * DI_BIAS))

            if ((iMinX <= iCrosX) && (iCrosX <= iMaxX) && (iMinY <= iCrosY) && (iCrosY <= iMaxY)) {
                //領域内で交わる
                Min.longitude = sp.longitude < ep.longitude ? sp.longitude : ep.longitude
                Max.longitude = sp.longitude > ep.longitude ? sp.longitude : ep.longitude
                Min.latitude = sp.latitude < ep.latitude ? sp.latitude : ep.latitude
                Max.latitude = sp.latitude > ep.latitude ? sp.latitude : ep.latitude
                iMinX = Int(round(Min.longitude * DI_BIAS))
                iMinY = Int(round(Min.latitude * DI_BIAS))
                iMaxX = Int(round(Max.longitude * DI_BIAS))
                iMaxY = Int(round(Max.latitude * DI_BIAS))
                if ((iMinX <= iCrosX) && (iCrosX <= iMaxX) && (iMinY <= iCrosY) && (iCrosY <= iMaxY)) {
                    //線分内で交わる
                    return 1
                } else {
                    return 0
                }
            } else {
                return 0
            }
        }
        return 0
    }
}
