読者です 読者をやめる 読者になる 読者になる

C#でDbGeographyに定義したPolygonの巻きを補正する

こんにちは。beaglesoftの真鍋です。

SQL ServerでGeographyのポリゴンを扱う場合には右巻/左巻で世界が一変するわけで、結構重要です。普通はSQL Serverで以下のクエリを発行すればよしなに行ってくれます。

-- geomがポリゴンを保存しているGeeography型の列

-- 巻きが逆の場合に補正します
update `テーブル名` set [geom] = [geom].MakeValid().ReorientObject() where [geom].MakeValid().EnvelopeAngle() > 90

-- 有効なインスタンスに補正します
update `テーブル名` set [geom] = [geom].MakeValid() where [geom].STIsValid() = 0

ただ、EntityFrameworkを使っているとこの処理だけSQLを実行するのも手間です。そこで、DbGeography型で対応できないかと調べたのですが、現在のところDbGeometory型に変換してからでないと処理ができないことがわかりました。

うーんと思っていたところ、どうやら以下の方法で同じことができることがわかりました。

/// <summary>
/// 経度 緯度,経度 緯度...の文字列からポリゴンを生成します。
/// </summary>
/// <param name="polygonText">経度 緯度,経度 緯度...の文字列</param>
/// <returns>DbGeoGraphyのインスタンス</returns>
public static DbGeography ConvertToPolygon(string polygonText)
{
    if (string.IsNullOrEmpty(polygonText))
    {
        throw new ArgumentNullException();
    }

    var sqlGeography = SqlGeography.STGeomFromText(new SqlChars($"POLYGON(({polygonText}))"), 4326).MakeValid();
    if (sqlGeography.EnvelopeAngle() > 90)
    {
        sqlGeography = sqlGeography.ReorientObject();
    }

    return DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography);
}

行っていることはSQLと同じですので省略しますが、これは便利です。SqlGeographyはMicrosoft.SqlServer.Types 名前空間に定義されているクラスで、DbGeographyよりも多くの処理が定義されているようです。もともとWKTを介してデータの変換は DbGeometory⇔DbGeography と行えるのですが、こっちはより便利ですね。

参考にしたサイト

How to correct Polygon Ring Orientation using C# Entity Framework 5 DbGeography Spatial Data - Stack Overflow

DbGeography Reverse Polygon Point Order (Ring Orientation) With Entity Framework 5, WKTString and MSSQL 2008 | Tales of a White Robe