PowerShellで緯度経度と平面直角座標系を変換する。

国土地理院のサイトで緯度経度と平面直角座標系を変換する事ができます。
業務に使う用に作ったのですがひょっとしたら需要があるかもしれないのでここに置いておきます。
平面直角座標系でつかう基準点を間違うととんでもない数値になるので注意してください。

#度分秒から10進数度単位への変換
```.ps1
Function Convert-DegreesToDecimalDegrees{
  [outputtype([Double])]
  param(
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = '度'
    )]
    [int]$degree,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = '分'
    )]
    [ValidateRange(0,59)]
    [int]$minute,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = '秒'
    )]
    [Double]$second,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'StringValue',
      helpmessage = '度分秒を文字列で'
    )]
    [string]$degreesString
  )
  begin{
    $tmp = 10000000000000000;
  }
  process{
    #パラメータセット
    switch($PsCmdlet.ParameterSetName){
      'StringValue'{
        if(
            $degreesString -match '(?<degree>\d+)°(?<minute>\d+)′(?<second>\d+[\.\d+]*)″'
        ){
            $degree = [int]$Matches.degree;
            $minute = [int]$Matches.minute;
            $second = [double]$Matches.second;
        }
        elseif(
            $degreesString -match '(?<degree>\d+)度(?<minute>\d+)分(?<second>\d+[\.\d+]*)秒' 
        ){
            $degree = [int]$Matches.degree;
            $minute = [int]$Matches.minute;
            $second = [double]$Matches.second;
        }
        else{
            throw 'not match degrees string'
        }
        break;
      }
      'NumericValue'{break}
    } 
    #微小単位が消えないよう桁あげて分秒計算
    [Double]$c = ((($minute*$tmp)+(($second*$tmp)/60))/60) / $tmp;
    [Double]$decimalDegree = $degree + $c;
    return $decimalDegree
  }
}
```
#10進度単位から度単位への変換
```.ps1
function Convert-DecimalDegreesToDegrees{
  [outputtype([Management.Automation.PSCustomObject])]
  param($decimalDegree)
  $degSplited =  $("$decimalDegree").split('.');
  $degree     = [int]$degSplited[0] ;
  $mintmp     = [double]::Parse($('0.'+ ($degSplited[1]))) * 60;
  $minSplited = $("$mintmp").split('.');
  $minute     = [int]$minSplited[0];
  $secondtmp  = [Double]::Parse($('0.'+ ($minSplited[1]))) * 60;
  $second     = [Math]::Round($secondtmp,12);
  $obj = [pscustomobject]@{degree=$degree;minute=$minute;second=$second;decimalDegree=$decimalDegree};
  return $obj
}
```
#平面直角座標・緯度経度変換
```.ps1
function Convert-LatLongToXY {
  [outputtype([array])]
  param(
    [parameter(
      mandatory = $false,
      helpmessage = '測地系		=1[日本測地系] =2[世界測地系]'
    )]
    $refFrame = 2,
    [parameter(
      mandatory = $true,
      ValueFromPipelineByPropertyName = $true,    
      helpmessage = '系番号(平面直角座標系)		系番号1〜19を指定する'
    )]
    [ValidateRange(1,19)]
    [int]$zone = 9,
    [parameter(
      mandatory = $true,
      ValueFromPipelineByPropertyName = $true,
      helpmessage = '緯度		degree'
    )]
    [double]$latitude = 0,
    [parameter(
      mandatory = $true,
      ValueFromPipelineByPropertyName = $true,    
      helpmessage = '経度		degree'
    )]
    [double]$longitude = 0
  )
  begin{
    $url = 'http://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/bl2xy.pl?'
  }
  process{
    $request = "refFrame=$refFrame&zone=$zone&latitude=$latitude&longitude=$longitude&outputType=json"
    $requestURI = $url + $request  ;
    $result = Invoke-RestMethod -Uri $requestURI -DisableKeepAlive;
    if("$($result.ExportData.ErrMsg)" -ne ''){
      throw $result.ExportData.ErrMsg;
    };
    $answer = [pscustomobject]$result.OutPutData;
    $answer|Add-Member -NotePropertyName zone -NotePropertyValue $zone -PassThru;
  }
  end{
    return 
  }
}
```
#直角座標から緯度経度
```.ps1
function Convert-XYToLatLong {
  [outputtype([pscustomobject])]
  param(
    [parameter(
      mandatory = $false,
      helpmessage = '測地系		=1[日本測地系] =2[世界測地系]'
    )]
    $refFrame = 2,
    [parameter(
      mandatory = $true,
      ValueFromPipelineByPropertyName = $true,    
      helpmessage = '系番号(平面直角座標系)		系番号1〜19を指定する'
    )]
    [ValidateRange(1,19)]
    [int]$zone = 9,
    [parameter(
      mandatory = $true,
      ValueFromPipelineByPropertyName = $true,
      helpmessage = 'X座標		小数点以下3桁まで指定可(m)'
    )]
    [double]$publicX = 0,
    [parameter(
      mandatory = $true,
      ValueFromPipelineByPropertyName = $true,    
      helpmessage = 'Y座標		小数点以下3桁まで指定可(m)'
    )]
    [double]$publicY = 0
  )
  begin{   
    $url = 'http://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/xy2bl.pl?';
  }
  process{
    $publicX = [Math]::Round($publicX,3);
    $publicY = [Math]::Round($publicY,3);
    $request = "refFrame=$refFrame&zone=$zone&publicX=$publicX&publicY=$publicY&outputType=json";
    $requestURI = $url + $request  ;
    $result = Invoke-RestMethod -Uri $requestURI -DisableKeepAlive;
    if("$($result.ExportData.ErrMsg)" -ne ''){
      throw $result.ExportData.ErrMsg;
    };
    $answer = [pscustomobject]$result.OutPutData;
    $answer|Add-Member -NotePropertyName zone -NotePropertyValue $zone -PassThru;
  }
  end{
    return 
  }
}
```
#直角座標での距離を求める
```.ps1
function Get-XYLength {
  [CmdletBinding()]
  [outputtype([pscustomobject])]
  param(
    [parameter(
      ParameterSetName = 'NumericValue',
      mandatory = $false,
      helpmessage = '測地系		=1[日本測地系] =2[世界測地系]'
    )]
    [parameter(
      ParameterSetName = 'ObjectValue',
      mandatory = $false,
      helpmessage = '測地系		=1[日本測地系] =2[世界測地系]'
    )]    
    $refFrame = 2,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = '系番号(平面直角座標系)		系番号1〜19を指定する'
    )]
    [ValidateRange(1,19)]
    [int]$zone = 9,
    [parameter(
      mandatory = $true,
      helpmessage = 'X座標		小数点以下3桁まで指定可(m)'
    )]
    [double]$publicX1 = 0,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = 'Y座標		小数点以下3桁まで指定可(m)'
    )]
    [double]$publicY1 = 0,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = 'X座標		小数点以下3桁まで指定可(m)'
    )]
    [double]$publicX2 = 0,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = 'Y座標		小数点以下3桁まで指定可(m)'
    )]
    [double]$publicY2 = 0,
    
    [parameter(
      mandatory = $true,
      ParameterSetName = 'ObjectValue'
    )]
    [pscustomobject]$object1,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'ObjectValue'
    )]
    [pscustomobject]$object2 
  )
  begin{
    $url = 'http://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/xy2st.pl?';
  }
  process{
    switch($PsCmdlet.ParameterSetName){
      'ObjectValue'{
        if($object1.zone -eq $object2.zone){
          $zone = $object1.zone ;
        }
        else{
          throw 'not mach Objects zone property';
        }
        $publicX1 = $object1.publicX;
        $publicY1 = $object1.publicY;     
        $publicX2 = $object2.publicX;
        $publicY2 = $object2.publicY;
        break;
      }
      'NumericValue'{break}
    }  
    $publicX1 = [Math]::Round($publicX1,3);
    $publicY1 = [Math]::Round($publicY1,3);
    $publicX2 = [Math]::Round($publicX2,3);
    $publicY2 = [Math]::Round($publicY2,3);
    $request = "refFrame=$refFrame&zone=$zone&publicX1=$publicX1&publicY1=$publicY1&publicX2=$publicX2&publicY2=$publicY2&outputType=json";
    $requestURI = $url + $request  ;
    $result = Invoke-RestMethod -Uri $requestURI -DisableKeepAlive;
    if("$($result.ExportData.ErrMsg)" -ne ''){
      throw $result.ExportData.ErrMsg;
    };
    $answer = [pscustomobject]$result.OutPutData;
    $answer
  }
  end{
    return 
  }
}
```
#世界測地系で直線距離
```.ps1
function Get-LatLongLength {
  [CmdletBinding()]
  [outputtype([pscustomobject])]
  param(
    [parameter(
      mandatory = $false,
      ParameterSetName = 'ObjectValue',
      helpmessage = 'GRS80（世界測地系）かbessel（日本測地系）を指定。初期値は世界測地系'
    )]
    [parameter(
      mandatory = $false,
      ParameterSetName = 'NumericValue',
      helpmessage = 'GRS80（世界測地系）かbessel（日本測地系）を指定。初期値は世界測地系'
    )]
    [ValidateSet('GRS80','bessel')]
    [string]$ellipsoid = 'GRS80',
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = '出発点緯度 degree'
    )]
    [double]$latitude1 = 0,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = '出発点経度 degree'
    )]
    [double]$longitude1 = 0,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = '到着点緯度 degree'
    )]
    [double]$latitude2 = 0,
    [parameter(
      mandatory = $true,
      ParameterSetName = 'NumericValue',
      helpmessage = '到着点経度 degree'
    )]
    [double]$longitude2 = 0,    
    [parameter(
      mandatory = $true,
      ParameterSetName = 'ObjectValue'
    )]
    [pscustomobject]$object1,
    [parameter(
      mandatory = $true,
      position = 1,
      ParameterSetName = 'ObjectValue'
    )]
    [pscustomobject]$object2 
  )
  begin{
    $url = 'http://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/bl2st_calc.pl?';
  }
  process{
    switch($PsCmdlet.ParameterSetName){
      'ObjectValue'{
        $latitude1  = $object1.latitude;
        $longitude1 = $object1.longitude;
        $latitude2  = $object2.latitude;
        $longitude2 = $object2.longitude;
        break;
      }
      'NumericValue'{break}
    }
    $request    = "ellipsoid=$ellipsoid&latitude1=$latitude1&longitude1=$longitude1&latitude2=$latitude2&longitude2=$longitude2&outputType=json";
    $requestURI = $url + $request  ;
    $result = Invoke-RestMethod -Uri $requestURI -DisableKeepAlive;
    if("$($result.ExportData.ErrMsg)" -ne ''){
      throw $result.ExportData.ErrMsg;
    };
    $answer = [pscustomobject]$result.OutPutData;
    $answer
  }
  end{
    return 
  }
}
```