LoginSignup
1
4

More than 5 years have passed since last update.

C#からSpatiaLiteを利用する

Last updated at Posted at 2017-11-23

SpatiaLiteとはSQLite上でGISを扱えるようにする拡張モジュールです。
PostgreSQLに対するPostGISのような位置づけです。

さて、こいつをC#で動かそうとすると、なかなか大変だったので、忘れないように手順をメモしておきます。

前提

今回は、

  • Visual Studio (VS)でC#のプロジェクトを作る。
  • そのプロジェクトのSystem.Data.SQLite上でSpatiaLiteの拡張モジュールをロードし使う
  • x64向けビルドを作る

という前提で話を進めます。

System.Data.SQLiteの準備

まずは、VSでSQLiteが使えるように、C#のプロジェクトのNuGetでSystem.Data.SQLiteをインストールします。
また、構成マネージャを使ってx64ビルドを作れるようにしておきます。

SpatiaLiteのダウンロード

SpatiaLite本家サイト:http://www.gaia-gis.it/gaia-sins/

こちらのページの最下部にMS Windows binariesというコーナーがあるので、そこのcurrent stable versionのamd64を選択します。

image.png

ファイル一覧ページに遷移したら、mod_spatialite-4.3.0a-win-amd64.7zを選択してダウンロード。
image.png

で、この7zファイルを解凍すると、mod_spatialite.dll以下、依存するdll群がまとめて入っているわけですが、こいつが曲者。
このモジュールたちをそのまま使おうとすると、mod_spatialite.dllのロード時にlibstdc++_64-6.dllでアクセスバイオレーションが発生して落ちます。

そこで、アクセスバイオレーションの発生しないものに置き換える必要があります。

libstdc++_64-6.dllの置き換え

libstdc++_64-6.dllは、MinGW64に同梱されています。

MinGW64: https://sourceforge.net/projects/mingw-w64/?source=typ_redirect

上記サイトからダウンロードし、インストールします。こんな感じの設定でインストールしました。
image.png

インストールが完了したら、インストール先ディレクトリのbin以下の「libstdc++-6.dll」「libgcc_s_seh-1.dll」の2つのファイルをコピーし、前節のspatialiteのdll群のものと入れ替えます。

この2つのファイルをコピーします。
image.png

置き換える際、ファイル名は以下のようにしてください。

  • 「libstdc++-6.dll」はmod_spatialiteに同梱されているほうに合わせて「libstdc++_64-6.dll」にリネームする。
  • 「libgcc_s_seh-1.dll」は「libgcc_s_seh-1.dll」のままにする。

「libgcc_s_seh-1.dll」のほうもmod_spatialite同梱版に合わせた名前にリネームすると、私の環境では正しく動作しませんでした。
まぎらわしいので、もともと同梱されていた「libgcc_s_seh_64-1.dll」は削除してしまいましょう。

dll入れ替え後のディレクトリはこんな感じになります。
image.png
図中、選択されている2ファイルが入れ替えたものです。

なお、このdll入れ替えについては以下を参考にしました。
http://blog.jrg.com.br/2016/04/25/Fixing-spatialite-loading-problem/

ビルドイベントの設定

mod_spatialite.dll以下関連dll群を、VSのC#プロジェクトが出力するexeと同じディレクトリにコピーするようビルドイベントを設定します。

プロジェクトのプロパティの「ビルドイベント」から、「ビルド後イベントのコマンドライン」に以下のような感じで書いておきましょう。

copy <mod_spatialiteのあるディレクトリ>\*.dll $(ProjectDir)$(OutDir)

動作確認

さて、ではいよいよ動作確認です。以下のようなコードを実行してみましょう。

using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace cstx64
{
    class Program
    {
        private static void ExecuteNonQuery(string sql, SQLiteConnection conn)
        {
            using (var command = new SQLiteCommand(sql, conn))
            {
                command.ExecuteNonQuery();
            }
        }

        private static void ExecuteScalar(string sql, SQLiteConnection conn)
        {
            using (var command = new SQLiteCommand(sql, conn))
            {
                command.ExecuteScalar();
            }
        }

        private static void LoadExtension(SQLiteConnection conn)
        {
            var modulePath = @"mod_spatialite";
            conn.EnableExtensions(true);
            conn.LoadExtension(modulePath);
        }

        public static void Main(string[] args)
        {
            string fileName = @".\test.db";
            if (System.IO.File.Exists(fileName))
            {
                System.IO.File.Delete(fileName);
            }

            using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3", fileName)))
            {
                conn.Open();

                LoadExtension(conn);
                string sql = " CREATE TABLE IF NOT EXISTS t (id, name, point)";
                ExecuteNonQuery(sql, conn);

                sql = "INSERT INTO t VALUES(1, 'some', ST_GeomFromText('POINTZ(1.0 2.0 3.0)'))";
                ExecuteNonQuery(sql, conn);

                sql = "SELECT id, name, ST_AsText(point) AS pt FROM t";
                using (var command = new SQLiteCommand(sql, conn))
                {
                    using (var dr = command.ExecuteReader())
                    {
                        while (dr.Read())
                        {
                            int id = Convert.ToInt32(dr["id"].ToString());
                            string name = dr["name"].ToString();
                            string pt = dr["pt"].ToString(); ;
                            Console.WriteLine(String.Format("{0}, {1}, {2}", id, name, pt));
                        }
                    }
                }
                conn.Close();
            }
        }
    }
}


LoadExtensionメソッドで、SpatiaLiteの拡張モジュールをロードしています。
ロードする前にEnableExtensionsで拡張を有効化しなければならないので注意です。

(さらにいうと、このEnableExtensionsLoadExtensionの操作は、SQLiteConnectionインスタンスごとに実行する必要があります。)

ロードが正しく行われると、その後のINSERT文、SELECT文にあるようなST_xxxの関数が利用できるようになります。
ここでは適当に座標が(1,2,3)の点をpointカラムに投入し、それが取れていることを確認しています。

1, some, POINT Z(1 2 3)

これで動作確認完了です。お疲れさまでした。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4