【C#】Proj.Net ~ 環境構築編 / .NET で PROJ を使うには... ~

■ PROJ

 * 地理座標系と投影座標系との間の変換を行うライブラリ
 * 詳細は、以下の関連記事を参照。
https://blogs.yahoo.co.jp/dk521123/38095650.html

.NETで PROJ を使うには...

【1】Proj.Net << 今回のテーマ
【2】Proj4Net

etc...
設定方法
 * どちらもNuGetでインストール可能(簡単)

【1】Proj.Net

公式サイト
https://archive.codeplex.com/?p=projnet
ライセンス
# 上記からダウンロードした中の「license.json」より抜粋

GNU Library General Public License (LGPL)
コード例
http://romansk.blogspot.com/2013/09/converting-latitude-and-longitude-to.html
メモ
 * GeoAPI も含まれてインストールされる

【2】Proj4Net

公式サイト
https://archive.codeplex.com/?p=proj4net
メモ
 * 「【2】Proj4Net」だとコードのサンプルが乏しい(っつーかーない?)

■ サンプル

例1:緯度経度 ⇔ UTM(Universal Transverse Mercator)に変換

using GeoAPI.CoordinateSystems;
using GeoAPI.CoordinateSystems.Transformations;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;
using System;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      double latitude = double.Parse(this.textBox1.Text);
      double longitude = double.Parse(this.textBox2.Text);
      if (TryParseUtm(latitude, longitude, out double utmX, out double utmY))
      {
        this.label1.Text = string.Format("({0}, {1}) => ({2}, {3})", latitude, longitude, utmX, utmY);
      }
      else
      {
        this.label1.Text = "Failed...";
      }
    }

    private void button2_Click(object sender, EventArgs e)
    {
      int zone = int.Parse(this.textBox1.Text);
      double utmX = double.Parse(this.textBox2.Text);
      double utmY = double.Parse(this.textBox3.Text);
      if (TryParseLatLon(zone, utmX, utmY, out double latitude, out double longitude))
      {
        this.label1.Text = string.Format("({0}, {1}) => ({2}, {3})", utmX, utmY, latitude, longitude);
      }
      else
      {
        this.label1.Text = "Failed...";
      }
    }

    public static bool TryParseUtm(double latitude, double longitude, out double easting, out double northing)
    {
      try
      {
        int zone = GetZone(latitude, longitude);

        // Transform to UTM
        CoordinateTransformationFactory factory = new CoordinateTransformationFactory();
        ICoordinateSystem wgs84geo = GeographicCoordinateSystem.WGS84;
        // https://github.com/fivepmtechnology/ProjNET/blob/master/ProjNet.CoordinateSystems/CoordinateSystems/ProjectedCoordinateSystem.cs
        bool isNorthZone = latitude > 0;
        ICoordinateSystem utm = ProjectedCoordinateSystem.WGS84_UTM(zone, isNorthZone);
        ICoordinateTransformation transformation =
           factory.CreateFromCoordinateSystems(wgs84geo, utm);
        // ★ longitude(経度), latitude(緯度)の順番 ★
        double[] utmAxes = 
           transformation.MathTransform.Transform(new double[] { longitude, latitude });

        easting = utmAxes[0];
        northing = utmAxes[1];

        return true;
      }
      catch(Exception)
      {
      }
      easting = double.MinValue;
      northing = double.MinValue;
      return false;
    }

    public static bool TryParseLatLon(
      int zone, double easting, double northing, out double latitude, out double longitude)
    {
      try
      {
        // Transform from UTM
        CoordinateTransformationFactory factory = new CoordinateTransformationFactory();
        ICoordinateSystem wgs84geo = GeographicCoordinateSystem.WGS84;
        ICoordinateSystem utm = ProjectedCoordinateSystem.WGS84_UTM(zone, true);
        ICoordinateTransformation transformation = factory.CreateFromCoordinateSystems(wgs84geo, utm);
        IMathTransform inversedTransform = transformation.MathTransform.Inverse();
        double[] points = inversedTransform.Transform(new double[] { easting, northing });

        longitude = points[0];
        latitude = points[1];

        return true;
      }
      catch (Exception)
      {
      }
      longitude = double.MinValue;
      latitude = double.MinValue;
      return false;
    }

    private static int GetZone(double latitude, double longitude)
    {
      // Norway
      if (latitude >= 56 && latitude < 64 && longitude >= 3 && longitude < 13)
        return 32;

      // Spitsbergen
      if (latitude >= 72 && latitude < 84)
      {
        if (longitude >= 0 && longitude < 9)
          return 31;
        else if (longitude >= 9 && longitude < 21)
          return 33;
        if (longitude >= 21 && longitude < 33)
          return 35;
        if (longitude >= 33 && longitude < 42)
          return 37;
      }

      return (int)Math.Ceiling((longitude + 180) / 6);
    }
  }
}

補足

 * 確認には以下のサイトが役立つかも
緯度経度座標をUTM座標に変換する
http://asp.ncm-git.co.jp/QuickConvert/BL2UTM.aspx

■ 後で役立ちそうなサイト

http://mng.seedcollector.net/blog/?p=1140

自作で変換

https://stackoverflow.com/questions/2689836/converting-utm-wsg84-coordinates-to-latitude-and-longitude
「[C#]平面直角座標系と緯度経度を変換する
https://www.kobiwa.jp/2017/03/22/post-286/

メモ

https://www.oipapio.com/question-335705
で載っている「DotSpatial」が気になる

関連記事

C#】Proj.Net ~ 基本編 ~

https://blogs.yahoo.co.jp/dk521123/38104577.html

C#】Proj.Net ~ あれこれ編 ~

https://blogs.yahoo.co.jp/dk521123/38104620.html

GISツール】PROJ / PROJ.4 ~ 入門編 ~

https://blogs.yahoo.co.jp/dk521123/38095650.html

地図 / 緯度経度 について

https://blogs.yahoo.co.jp/dk521123/38081789.html