diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6718088..0ec4163 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,10 +5,11 @@ on: [push, pull_request] jobs: test: name: ${{ matrix.DB }} - runs-on: ubuntu-latest + runs-on: ${{ matrix.OS }} strategy: fail-fast: false matrix: + os: [ubuntu-latest] include: - DB: MsSql2012 (SQL Server 2017) DB_INIT: | @@ -55,6 +56,15 @@ jobs: DB_INIT: docker run -d -e POSTGRES_PASSWORD=nhsp_test -p 15433:5432 -v ./Tests.NHibernate.Spatial.PostGis30/initdb:/docker-entrypoint-initdb.d postgis/postgis:16-3.4 TEST_PROJECT: Tests.NHibernate.Spatial.PostGis30 + - DB: SpatiaLite (SpatiaLite 4) + DB_INIT: sudo apt install libsqlite3-mod-spatialite=4.* + TEST_PROJECT: Tests.NHibernate.Spatial.SpatiaLite + OS: ubuntu-20.04 + + - DB: SpatiaLite (SpatiaLite 5) + DB_INIT: sudo apt install libsqlite3-mod-spatialite=5.* + TEST_PROJECT: Tests.NHibernate.Spatial.SpatiaLite + steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/BuildPackages.bat b/BuildPackages.bat index c6d6087..691dafd 100644 --- a/BuildPackages.bat +++ b/BuildPackages.bat @@ -11,4 +11,5 @@ dotnet pack %options% NHibernate.Spatial dotnet pack %options% NHibernate.Spatial.MsSql dotnet pack %options% NHibernate.Spatial.MySQL REM dotnet pack %options% NHibernate.Spatial.Oracle +dotnet pack %options% NHibernate.Spatial.SpatiaLite dotnet pack %options% NHibernate.Spatial.PostGis diff --git a/NHibernate.Spatial.SpatiaLite/Dialect/SpatiaLiteDialect.cs b/NHibernate.Spatial.SpatiaLite/Dialect/SpatiaLiteDialect.cs new file mode 100644 index 0000000..e019fdc --- /dev/null +++ b/NHibernate.Spatial.SpatiaLite/Dialect/SpatiaLiteDialect.cs @@ -0,0 +1,542 @@ +using System; +using System.Globalization; +using System.Text; +using NHibernate.Dialect; +using NHibernate.Spatial.Dialect.Function; +using NHibernate.Spatial.Metadata; +using NHibernate.Spatial.Type; +using NHibernate.SqlCommand; +using NHibernate.Type; + +namespace NHibernate.Spatial.Dialect +{ + public class SpatiaLiteDialect : SQLiteDialect, ISpatialDialect + { + private static readonly IType GeometryTypeInstance = new CustomType(typeof(SpatiaLiteGeometryType), null); + + public SpatiaLiteDialect() + { + SpatialDialect.LastInstantiated = this; + + RegisterBasicFunctions(); + RegisterFunctions(); + } + + public IType GeometryType => GeometryTypeInstance; + + public string MultipleQueriesSeparator => ";"; + + public IGeometryUserType CreateGeometryUserType() + { + return new SpatiaLiteGeometryType(); + } + + public SqlString GetSpatialTransformString(object geometry, int srid) + { + return new SqlStringBuilder() + .Add(SpatialDialect.IsoPrefix) + .Add("Transform(") + .AddObject(geometry) + .Add(",") + .Add(srid.ToString(NumberFormatInfo.InvariantInfo)) + .Add(")") + .ToSqlString(); + } + + public SqlString GetSpatialAggregateString(object geometry, SpatialAggregate aggregate) + { + string aggregateFunction; + switch (aggregate) + { + case SpatialAggregate.Collect: + aggregateFunction = "Collect"; + break; + case SpatialAggregate.ConvexHull: + return new SqlStringBuilder() + .Add(SpatialDialect.IsoPrefix) + .Add("Collect(") + .Add(SpatialDialect.IsoPrefix) + .Add(aggregate.ToString()) + .Add("(") + .AddObject(geometry) + .Add("))") + .ToSqlString(); + case SpatialAggregate.Envelope: + aggregateFunction = "Extent"; + break; + case SpatialAggregate.Intersection: + throw new NotSupportedException("SpatialAggregate Intersection"); // TODO Check whether collect works here too + case SpatialAggregate.Union: + aggregateFunction = "GUnion"; + break; + default: + throw new ArgumentException("Invalid spatial aggregate argument"); + } + return new SqlStringBuilder() + .Add(aggregateFunction) + .Add("(") + .AddObject(geometry) + .Add(")") + .ToSqlString(); + } + + public SqlString GetSpatialAnalysisString(object geometry, + SpatialAnalysis analysis, + object extraArgument) + { + switch (analysis) + { + case SpatialAnalysis.Buffer: + if (!(extraArgument is Parameter || new SqlString(Parameter.Placeholder).Equals(extraArgument))) + { + extraArgument = Convert.ToString(extraArgument, NumberFormatInfo.InvariantInfo); + } + + return new SqlStringBuilder(6) + .Add(SpatialDialect.IsoPrefix) + .Add("Buffer(") + .AddObject(geometry) + .Add(", ") + .AddObject(extraArgument) + .Add(")") + .ToSqlString(); + case SpatialAnalysis.ConvexHull: + return new SqlStringBuilder() + .Add(SpatialDialect.IsoPrefix) + .Add("ConvexHull(") + .AddObject(geometry) + .Add(")") + .ToSqlString(); + case SpatialAnalysis.Difference: + case SpatialAnalysis.Distance: + case SpatialAnalysis.Intersection: + case SpatialAnalysis.SymDifference: + case SpatialAnalysis.Union: + return new SqlStringBuilder() + .Add(SpatialDialect.IsoPrefix) + .Add(analysis.ToString()) + .Add("(") + .AddObject(geometry) + .Add(",") + .AddObject(extraArgument) + .Add(")") + .ToSqlString(); + default: + throw new ArgumentException("Invalid spatial analysis argument"); + } + } + + public SqlString GetSpatialValidationString(object geometry, + SpatialValidation validation, + bool criterion) + { + return new SqlStringBuilder() + .Add(SpatialDialect.IsoPrefix) + .Add(validation.ToString()) + .Add("(") + .AddObject(geometry) + .Add(")") + .Add(" = 1") + .ToSqlString(); + } + + public SqlString GetSpatialRelateString(object geometry, + object anotherGeometry, + object pattern, + bool isStringPattern, + bool criterion) + { + var relateString = new SqlStringBuilder() + .Add(SpatialDialect.IsoPrefix) + .Add("Relate(") + .AddObject(geometry) + .Add(",") + .AddObject(anotherGeometry) + .Add(","); + + if (isStringPattern) + { + relateString.Add("'"); + } + + relateString.Add(pattern.ToString()); + + if (isStringPattern) + { + relateString.Add("'"); + } + + relateString.Add(")"); + + return relateString.ToSqlString(); + } + + public SqlString GetSpatialRelationString(object geometry, + SpatialRelation relation, + object anotherGeometry, + bool criterion) + { + switch (relation) + { + case SpatialRelation.Covers: + string[] patterns = + { + "T*****FF*", + "*T****FF*", + "***T**FF*", + "****T*FF*" + }; + var builder = new SqlStringBuilder(); + builder.Add("("); + for (int i = 0; i < patterns.Length; i++) + { + if (i > 0) + { + builder.Add(" OR "); + } + builder + .Add(SpatialDialect.IsoPrefix) + .Add("Relate") + .Add("(") + .AddObject(geometry) + .Add(", ") + .AddObject(anotherGeometry) + .Add(", '") + .Add(patterns[i]) + .Add("')") + .ToSqlString(); + } + builder.Add(")"); + return builder.ToSqlString(); + case SpatialRelation.CoveredBy: + return GetSpatialRelationString(anotherGeometry, SpatialRelation.Covers, geometry, criterion); + case SpatialRelation.EqualsExact: + throw new ArgumentOutOfRangeException(nameof(relation), relation, "Unsupported spatial relation"); + default: + return new SqlStringBuilder(6) + .Add(SpatialDialect.IsoPrefix) + .Add(relation.ToString()) + .Add("(") + .AddObject(geometry) + .Add(", ") + .AddObject(anotherGeometry) + .Add(")") + .ToSqlString(); + } + } + + public SqlString GetSpatialRelationString(object geometry, SpatialRelation relation, object anotherGeometry, object parameter, bool criterion) + { + switch (relation) + { + case SpatialRelation.IsWithinDistance: + return new SqlStringBuilder() + .Add("PtDistWithin") + .Add("(") + .AddObject(geometry) + .Add(", ") + .AddObject(anotherGeometry) + .Add(", ") + .Add(parameter.ToString()) + .Add(")") + .ToSqlString(); + case SpatialRelation.Relate: + return new SqlStringBuilder() + .Add(SpatialDialect.IsoPrefix) + .Add(relation.ToString()) + .Add("(") + .AddObject(geometry) + .Add(", ") + .AddObject(anotherGeometry) + .Add(", '") + .Add(parameter.ToString()) + .Add("')") + .ToSqlString(); + default: + throw new ArgumentOutOfRangeException(nameof(relation), relation, "Unsupported spatial relation"); + } + } + + public SqlString GetSpatialFilterString(string tableAlias, + string geometryColumnName, + string primaryKeyColumnName, + string tableName, + Parameter parameter) + { + return new SqlStringBuilder(30) + .Add("(MbrIntersects(") + .Add(tableAlias) + .Add(".") + .Add(geometryColumnName) + .Add(", ") + .Add(parameter) + .Add("))") + .ToSqlString(); + } + + public SqlString GetSpatialFilterString(string tableAlias, + string geometryColumnName, + string primaryKeyColumnName, + string tableName) + { + return new SqlStringBuilder(30) + .Add("(MbrIntersects(") + .Add(tableAlias) + .Add(".") + .Add(geometryColumnName) + .Add(", ") + .AddParameter() + .Add("))") + .ToSqlString(); + } + + /// + /// Gets the spatial create string. + /// + /// The schema. + /// + public string GetSpatialCreateString(string schema) + { + return string.Empty; + } + + /// + /// Gets the spatial create string. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public string GetSpatialCreateString(string schema, + string table, + string column, + int srid, + string subtype, + int dimension, + bool isNullable) + { + var builder = new StringBuilder(); + + builder.AppendFormat("ALTER TABLE {0} DROP COLUMN {1};" + , QuoteForTableName(table) + , QuoteForColumnName(column) + ); + + builder.AppendFormat("SELECT AddGeometryColumn('{0}','{1}',{2},'{3}','{4}', {5});", + table, column, srid, subtype, ToXyzm(dimension), isNullable ? 0 : 1); + + builder.AppendFormat("SELECT CreateSpatialIndex('{0}','{1}');", table, column); + + return builder.ToString(); + } + + /// + /// Gets the spatial drop string. + /// + /// The schema. + /// + public string GetSpatialDropString(string schema) + { + return string.Empty; + } + + /// + /// Gets the spatial drop string. + /// + /// The schema. + /// The table. + /// The column. + /// + public string GetSpatialDropString(string schema, string table, string column) + { + var builder = new StringBuilder(); + + builder.AppendFormat("SELECT DisableSpatialIndex('{0}','{1}');", + table, column); + + builder.AppendFormat("SELECT DiscardGeometryColumn('{0}','{1}');", + table, column); + + builder.AppendFormat("DELETE FROM geometry_columns where f_table_name = '{0}' AND f_geometry_column = '{1}';", + table, column); + + return builder.ToString(); + } + + public bool SupportsSpatialMetadata(MetadataClass metadataClass) + { + return true; + } + + private static string ToXyzm(int dimension) + { + switch (dimension) + { + case 3: + return "XYZ"; + case 4: + return "XYZM"; + } + return "XY"; + } + + private static int ToGeometryType(string subtype) + { + switch (subtype) + { + case "POINT": + return 1; + case "LINESTRING": + return 2; + case "POLYGON": + return 3; + case "MULTIPOINT": + return 4; + case "MULTILINESTRING": + return 5; + case "MULTIPOLYGON": + return 6; + case "GEOMETRYCOLLECTION": + return 7; + default: + throw new Exception("Should never reach here"); + } + } + + #region Functions registration + + private void RegisterBasicFunctions() + { + // Relations + RegisterSpatialFunction(SpatialRelation.Contains); + RegisterSpatialFunction(SpatialRelation.CoveredBy); + RegisterSpatialFunction(SpatialRelation.Covers); + RegisterSpatialFunction(SpatialRelation.Crosses); + RegisterSpatialFunction(SpatialRelation.Disjoint); + RegisterSpatialFunction(SpatialRelation.Equals); + RegisterSpatialFunction(SpatialRelation.Intersects); + RegisterSpatialFunction(SpatialRelation.Overlaps); + RegisterSpatialFunction(SpatialRelation.Touches); + RegisterSpatialFunction(SpatialRelation.Within); + + // Analysis + RegisterSpatialFunction(SpatialAnalysis.Buffer); + RegisterSpatialFunction(SpatialAnalysis.ConvexHull); + RegisterSpatialFunction(SpatialAnalysis.Difference); + RegisterSpatialFunction(SpatialAnalysis.Distance); + RegisterSpatialFunction(SpatialAnalysis.Intersection); + RegisterSpatialFunction(SpatialAnalysis.SymDifference); + RegisterSpatialFunction(SpatialAnalysis.Union); + + // Validations + RegisterSpatialFunction(SpatialValidation.IsClosed); + RegisterSpatialFunction(SpatialValidation.IsEmpty); + RegisterSpatialFunction(SpatialValidation.IsRing); + RegisterSpatialFunction(SpatialValidation.IsSimple); + RegisterSpatialFunction(SpatialValidation.IsValid); + } + + protected override void RegisterFunctions() + { + RegisterSpatialFunction("Boundary"); + RegisterSpatialFunction("Centroid"); + RegisterSpatialFunction("EndPoint"); + RegisterSpatialFunction("Envelope"); + RegisterSpatialFunction("ExteriorRing"); + RegisterSpatialFunction("GeometryN", 2); + RegisterSpatialFunction("InteriorRingN", 2); + RegisterSpatialFunction("PointN", 2); + RegisterSpatialFunction("PointOnSurface"); + RegisterSpatialFunction("Simplify", 2); + RegisterSpatialFunction("StartPoint"); + RegisterSpatialFunction("Transform", 2); + + RegisterSpatialFunction("GeomCollFromText", 2); + RegisterSpatialFunction("GeomCollFromWKB", 2); + RegisterSpatialFunction("GeomFromText", 2); + RegisterSpatialFunction("GeomFromWKB", 2); + RegisterSpatialFunction("LineFromText", 2); + RegisterSpatialFunction("LineFromWKB", 2); + RegisterSpatialFunction("PointFromText", 2); + RegisterSpatialFunction("PointFromWKB", 2); + RegisterSpatialFunction("PolyFromText", 2); + RegisterSpatialFunction("PolyFromWKB", 2); + RegisterSpatialFunction("MLineFromText", 2); + RegisterSpatialFunction("MLineFromWKB", 2); + RegisterSpatialFunction("MPointFromText", 2); + RegisterSpatialFunction("MPointFromWKB", 2); + RegisterSpatialFunction("MPolyFromText", 2); + RegisterSpatialFunction("MPolyFromWKB", 2); + + RegisterSpatialFunction("AsBinary", NHibernateUtil.Binary); + + RegisterSpatialFunction("AsText", NHibernateUtil.String); + RegisterSpatialFunction("AsGML", NHibernateUtil.String); + RegisterSpatialFunction("GeometryType", NHibernateUtil.String); + + RegisterSpatialFunction("Area", NHibernateUtil.Double); + RegisterSpatialFunction("Length", "GLength", NHibernateUtil.Double); + RegisterSpatialFunction("X", NHibernateUtil.Double); + RegisterSpatialFunction("Y", NHibernateUtil.Double); + + RegisterSpatialFunction("SRID", NHibernateUtil.Int32); + RegisterSpatialFunction("Dimension", NHibernateUtil.Int32); + RegisterSpatialFunction("NumGeometries", NHibernateUtil.Int32); + RegisterSpatialFunction("NumInteriorRings", "NumInteriorRing", NHibernateUtil.Int32); + RegisterSpatialFunction("NumPoints", NHibernateUtil.Int32); + + RegisterSpatialFunction("Relate", NHibernateUtil.Boolean, 3); + } + + private void RegisterSpatialFunction(string standardName, string dialectName, IType returnedType, int allowedArgsCount) + { + RegisterFunction(SpatialDialect.HqlPrefix + standardName, new SpatialStandardSafeFunction(dialectName, returnedType, allowedArgsCount)); + } + + private void RegisterSpatialFunction(string standardName, string dialectName, IType returnedType) + { + RegisterSpatialFunction(standardName, dialectName, returnedType, 1); + } + + private void RegisterSpatialFunction(string name, IType returnedType, int allowedArgsCount) + { + RegisterSpatialFunction(name, name, returnedType, allowedArgsCount); + } + + private void RegisterSpatialFunction(string name, IType returnedType) + { + RegisterSpatialFunction(name, name, returnedType); + } + + private void RegisterSpatialFunction(string name, int allowedArgsCount) + { + RegisterSpatialFunction(name, GeometryType, allowedArgsCount); + } + + private void RegisterSpatialFunction(string name) + { + RegisterSpatialFunction(name, GeometryType); + } + + private void RegisterSpatialFunction(SpatialRelation relation) + { + RegisterFunction(SpatialDialect.HqlPrefix + relation, new SpatialRelationFunction(this, relation)); + } + + private void RegisterSpatialFunction(SpatialValidation validation) + { + RegisterFunction(SpatialDialect.HqlPrefix + validation, new SpatialValidationFunction(this, validation)); + } + + private void RegisterSpatialFunction(SpatialAnalysis analysis) + { + RegisterFunction(SpatialDialect.HqlPrefix + analysis, new SpatialAnalysisFunction(this, analysis)); + } + + #endregion + } +} diff --git a/NHibernate.Spatial.SpatiaLite/Driver/SpatiaLiteDriver.cs b/NHibernate.Spatial.SpatiaLite/Driver/SpatiaLiteDriver.cs new file mode 100644 index 0000000..7c9bbb6 --- /dev/null +++ b/NHibernate.Spatial.SpatiaLite/Driver/SpatiaLiteDriver.cs @@ -0,0 +1,69 @@ +using System; +using System.Data; +using System.Data.Common; +using System.Data.SQLite; +using System.IO; +using System.Reflection; +using NHibernate.Driver; + +namespace NHibernate.Spatial.Driver +{ + public class SpatiaLiteDriver : SQLite20Driver + { + static SpatiaLiteDriver() + { + string path = Environment.GetEnvironmentVariable("PATH"); + if (path == null) + { + throw new InvalidOperationException("Cannot get PATH environment variable"); + } + + string executingDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath); + if (string.IsNullOrEmpty(executingDirectory)) + { + throw new InvalidOperationException("Cannot get executing directory"); + } + + string spatiaLitePath = Path.Combine(executingDirectory, "lib", "spatialite"); + if (Directory.Exists(spatiaLitePath) && !path.ToLower().Contains(spatiaLitePath)) + { + Environment.SetEnvironmentVariable("PATH", spatiaLitePath + Path.PathSeparator + path); + } + } + + public override DbConnection CreateConnection() + { + var cn = base.CreateConnection(); + cn.StateChange += ConnectionStateChangeHandler; + return cn; + } + + private static void ConnectionStateChangeHandler(object sender, StateChangeEventArgs e) + { + if (e.OriginalState != ConnectionState.Broken && + e.OriginalState != ConnectionState.Closed && + e.OriginalState != ConnectionState.Connecting || + e.CurrentState != ConnectionState.Open) + { + return; + } + + var connection = (SQLiteConnection) sender; + using (var command = connection.CreateCommand()) + { + command.CommandText = "PRAGMA foreign_keys = ON;"; + command.ExecuteNonQuery(); + + // NOTE: After upgrading System.Data.SQLite from 1.0.98.1 to 1.0.116.0, the + // "SELECT load_extension('mod_spatialite');" SQL query to load the + // extension failed with a "SQL logic error: not authorized" error. + // Therefore, need to use the SQLiteConnection method instead; see: + // https://www.sqlite.org/loadext.html + connection.LoadExtension("mod_spatialite"); + + command.CommandText = "SELECT InitSpatialMetadata(1) WHERE CheckSpatialMetadata() = 0;"; + command.ExecuteNonQuery(); + } + } + } +} diff --git a/NHibernate.Spatial.SpatiaLite/Metadata/GeometryColumn.SpatiaLiteDialect.hbm.xml b/NHibernate.Spatial.SpatiaLite/Metadata/GeometryColumn.SpatiaLiteDialect.hbm.xml new file mode 100644 index 0000000..daf3b85 --- /dev/null +++ b/NHibernate.Spatial.SpatiaLite/Metadata/GeometryColumn.SpatiaLiteDialect.hbm.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NHibernate.Spatial.SpatiaLite/Metadata/SpatiaLiteGeometryColumn.cs b/NHibernate.Spatial.SpatiaLite/Metadata/SpatiaLiteGeometryColumn.cs new file mode 100644 index 0000000..aa8600c --- /dev/null +++ b/NHibernate.Spatial.SpatiaLite/Metadata/SpatiaLiteGeometryColumn.cs @@ -0,0 +1,75 @@ +using System; + +namespace NHibernate.Spatial.Metadata +{ + public class SpatiaLiteGeometryColumn : GeometryColumn + { + /// + /// Gets or sets the geometry subtype. + /// + /// The subtype. + public override string Subtype + { + get + { + switch (GeometryType) + { + case 1: + return "POINT"; + case 2: + return "LINESTRING"; + case 3: + return "POLYGON"; + case 4: + return "MULTIPOINT"; + case 5: + return "MULTILINESTRING"; + case 6: + return "MULTIPOLYGON"; + case 7: + return "GEOMETRYCOLLECTION"; + } + throw new Exception("Should never reach here"); + } + set + { + switch (value) + { + case "POINT": + GeometryType = 1; + break; + case "LINESTRING": + GeometryType = 2; + break; + case "POLYGON": + GeometryType = 3; + break; + case "MULTIPOINT": + GeometryType = 4; + break; + case "MULTILINESTRING": + GeometryType = 5; + break; + case "MULTIPOLYGON": + GeometryType = 6; + break; + case "GEOMETRYCOLLECTION": + GeometryType = 7; + break; + default: + throw new Exception("Should never reach here"); + } + } + } + + /// + /// Gets or sets the integer value defining the . + /// + public virtual int GeometryType { get; set; } + + /// + /// Gets or sets a value indicating which (if any) spatial index is enabled. + /// + public virtual int SpatialIndex { get; set; } + } +} diff --git a/NHibernate.Spatial.SpatiaLite/Metadata/SpatiaLiteSpatialReferenceSystem.cs b/NHibernate.Spatial.SpatiaLite/Metadata/SpatiaLiteSpatialReferenceSystem.cs new file mode 100644 index 0000000..96423a9 --- /dev/null +++ b/NHibernate.Spatial.SpatiaLite/Metadata/SpatiaLiteSpatialReferenceSystem.cs @@ -0,0 +1,62 @@ +using System; + +namespace NHibernate.Spatial.Metadata +{ + /// + /// + /// + [Serializable] + public class SpatiaLiteSpatialReferenceSystem : SpatialReferenceSystem + { + private string _refSysName; + private string _proj4Text; + + /// + /// Initializes a new instance of the class. + /// + public SpatiaLiteSpatialReferenceSystem() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The SRID. + /// Name of the authority. + /// The authority srid. + /// The spatial reference system name. + /// The PROJ.4 text. + /// The well known text. + public SpatiaLiteSpatialReferenceSystem(int srid, + string authorityName, + int authoritySrid, + string refSysName, + string proj4Text, + string wellKnownText) + { + SRID = srid; + AuthorityName = authorityName; + AuthoritySRID = authoritySrid; + RefSysName = refSysName; + Proj4Text = proj4Text; + WellKnownText = wellKnownText; + } + + /// + /// Gets or sets the spatial reference system name. + /// + public virtual string RefSysName + { + get => _refSysName; + set => _refSysName = value; + } + + /// + /// Gets or sets the PROJ.4 text. + /// + public virtual string Proj4Text + { + get => _proj4Text; + set => _proj4Text = value; + } + } +} diff --git a/NHibernate.Spatial.SpatiaLite/Metadata/SpatialReferenceSystem.SpatiaLiteDialect.hbm.xml b/NHibernate.Spatial.SpatiaLite/Metadata/SpatialReferenceSystem.SpatiaLiteDialect.hbm.xml new file mode 100644 index 0000000..9ff7b87 --- /dev/null +++ b/NHibernate.Spatial.SpatiaLite/Metadata/SpatialReferenceSystem.SpatiaLiteDialect.hbm.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NHibernate.Spatial.SpatiaLite/NHibernate.Spatial.SpatiaLite.csproj b/NHibernate.Spatial.SpatiaLite/NHibernate.Spatial.SpatiaLite.csproj new file mode 100644 index 0000000..ef897e5 --- /dev/null +++ b/NHibernate.Spatial.SpatiaLite/NHibernate.Spatial.SpatiaLite.csproj @@ -0,0 +1,27 @@ + + + + + netstandard2.0 + NHibernate.Spatial + true + true + snupkg + + + + + + + + + + + + + + + + + + diff --git a/NHibernate.Spatial.SpatiaLite/Type/SpatiaLiteGeometryType.cs b/NHibernate.Spatial.SpatiaLite/Type/SpatiaLiteGeometryType.cs new file mode 100644 index 0000000..1ffcc9d --- /dev/null +++ b/NHibernate.Spatial.SpatiaLite/Type/SpatiaLiteGeometryType.cs @@ -0,0 +1,73 @@ +using System; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO; + +namespace NHibernate.Spatial.Type +{ + [Serializable] + public class SpatiaLiteGeometryType : GeometryTypeBase + { + private readonly GaiaGeoReader _reader; + private readonly GaiaGeoWriter _writer; + + /// + /// Initializes a new instance of the class. + /// + public SpatiaLiteGeometryType() + : base(NHibernateUtil.BinaryBlob) + { + _reader = new GaiaGeoReader(); + _writer = new GaiaGeoWriter(); + } + + /// + /// Convert a to a SpatiaLite byte array. + /// + /// + /// + protected override byte[] FromGeometry(object value) + { + var geometry = value as Geometry; + if (geometry == null) + { + return null; + } + + SetDefaultSRID(geometry); + + // Ensure HandleOrdinates is set according to the ordinality of the geometry + // https://github.com/NetTopologySuite/NetTopologySuite.IO.SpatiaLite/issues/7 + var ordinates = Ordinates.XY; + var coordinate = geometry.Coordinate; + if (coordinate != null && !double.IsNaN(coordinate.Z)) + { + ordinates |= Ordinates.Z; + } + if (coordinate != null && !double.IsNaN(coordinate.M)) + { + ordinates |= Ordinates.M; + } + _writer.HandleOrdinates = ordinates; + + return _writer.Write(geometry); + } + + /// + /// Convert a SpatiaLite byte array to a . + /// + /// + /// + protected override Geometry ToGeometry(object value) + { + if (!(value is byte[] bytes)) + { + return null; + } + + var geometry = _reader.Read(bytes); + SetDefaultSRID(geometry); + + return geometry; + } + } +} diff --git a/NHibernate.Spatial.sln b/NHibernate.Spatial.sln index 3a1198a..b7475c3 100644 --- a/NHibernate.Spatial.sln +++ b/NHibernate.Spatial.sln @@ -30,6 +30,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.NHibernate.Spatial.My EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.NHibernate.Spatial.PostGis30", "Tests.NHibernate.Spatial.PostGis30\Tests.NHibernate.Spatial.PostGis30.csproj", "{ED1D3C35-D6F7-44CD-88A6-8E9192F685A8}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NHibernate.Spatial.SpatiaLite", "NHibernate.Spatial.SpatiaLite\NHibernate.Spatial.SpatiaLite.csproj", "{83520F4F-5F22-4AA8-B5AA-9C38C742486E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.NHibernate.Spatial.SpatiaLite", "Tests.NHibernate.Spatial.SpatiaLite\Tests.NHibernate.Spatial.SpatiaLite.csproj", "{38892858-02D0-4C5F-BAF6-891D5BFD67DF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -80,6 +84,14 @@ Global {ED1D3C35-D6F7-44CD-88A6-8E9192F685A8}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED1D3C35-D6F7-44CD-88A6-8E9192F685A8}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED1D3C35-D6F7-44CD-88A6-8E9192F685A8}.Release|Any CPU.Build.0 = Release|Any CPU + {83520F4F-5F22-4AA8-B5AA-9C38C742486E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83520F4F-5F22-4AA8-B5AA-9C38C742486E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83520F4F-5F22-4AA8-B5AA-9C38C742486E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83520F4F-5F22-4AA8-B5AA-9C38C742486E}.Release|Any CPU.Build.0 = Release|Any CPU + {38892858-02D0-4C5F-BAF6-891D5BFD67DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38892858-02D0-4C5F-BAF6-891D5BFD67DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38892858-02D0-4C5F-BAF6-891D5BFD67DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38892858-02D0-4C5F-BAF6-891D5BFD67DF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index 0cea30e..d7032cb 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,21 @@ NHibernate binaries. ## NuGet Packages -| Package | Version | -|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| -| [NHibernate.Spatial.MsSql](https://www.nuget.org/packages/NHibernate.Spatial.MsSql) | [![NuGet Status](http://img.shields.io/nuget/v/NHibernate.Spatial.MsSql.svg?style=flat)](http://www.nuget.org/packages/NHibernate.Spatial.MsSql) | -| [NHibernate.Spatial.MySQL](https://www.nuget.org/packages/NHibernate.Spatial.MySQL) | [![NuGet Status](http://img.shields.io/nuget/v/NHibernate.Spatial.MySQL.svg?style=flat)](http://www.nuget.org/packages/NHibernate.Spatial.MySQL) | -| [NHibernate.Spatial.PostGis](https://www.nuget.org/packages/NHibernate.Spatial.PostGis) | [![NuGet Status](http://img.shields.io/nuget/v/NHibernate.Spatial.PostGis.svg?style=flat)](http://www.nuget.org/packages/NHibernate.Spatial.PostGis) | +| Package | Version | +|-----------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [NHibernate.Spatial.MsSql](https://www.nuget.org/packages/NHibernate.Spatial.MsSql) | [![NuGet Status](http://img.shields.io/nuget/v/NHibernate.Spatial.MsSql.svg?style=flat)](http://www.nuget.org/packages/NHibernate.Spatial.MsSql) | +| [NHibernate.Spatial.MySQL](https://www.nuget.org/packages/NHibernate.Spatial.MySQL) | [![NuGet Status](http://img.shields.io/nuget/v/NHibernate.Spatial.MySQL.svg?style=flat)](http://www.nuget.org/packages/NHibernate.Spatial.MySQL) | +| [NHibernate.Spatial.PostGis](https://www.nuget.org/packages/NHibernate.Spatial.PostGis) | [![NuGet Status](http://img.shields.io/nuget/v/NHibernate.Spatial.PostGis.svg?style=flat)](http://www.nuget.org/packages/NHibernate.Spatial.PostGis) | +| [NHibernate.Spatial.SpatiaLite](https://www.nuget.org/packages/NHibernate.Spatial.SpatiaLite) | [![NuGet Status](http://img.shields.io/nuget/v/NHibernate.Spatial.SpatiaLite.svg?style=flat)](http://www.nuget.org/packages/NHibernate.Spatial.SpatiaLite) | ## Supported Databases -| Package | Dialects | CI Tests | -|----------------------------|--------------------------|---------------------------------------------------------------------------------------| -| NHibernate.Spatial.MsSql | SQL Server 2012 | SQL Server 2017, SQL Server 2019, SQL Server 2022 | -| NHibernate.Spatial.MySQL | MySQL 5.7, MySQL 8.0 | MySQL 5.7, MySQL 8.0, MySQL 8.3 | -| NHibernate.Spatial.PostGis | PostGIS 2.0, PostGIS 3.0 | PostGIS 2.5 (PostgreSQL 12), PostGIS 3.0 (PostgreSQL 12), PostGIS 3.4 (PostgreSQL 16) | +| Package | Dialects | CI Tests | +|-------------------------------|--------------------------|---------------------------------------------------------------------------------------| +| NHibernate.Spatial.MsSql | SQL Server 2012 | SQL Server 2017, SQL Server 2019, SQL Server 2022 | +| NHibernate.Spatial.MySQL | MySQL 5.7, MySQL 8.0 | MySQL 5.7, MySQL 8.0, MySQL 8.3 | +| NHibernate.Spatial.PostGis | PostGIS 2.0, PostGIS 3.0 | PostGIS 2.5 (PostgreSQL 12), PostGIS 3.0 (PostgreSQL 12), PostGIS 3.4 (PostgreSQL 16) | +| NHibernate.Spatial.SpatiaLite | SpatiaLite | SpatiaLite 4, SpatiaLite 5 | ## Getting Started @@ -32,6 +34,15 @@ For general NHibernate help, the NHibernate community website - /lib/spatialite` to the +`PATH`. + +https://www.gaia-gis.it/fossil/libspatialite/wiki?name=mod_spatialite + ## Discussion Forum Discussion is made through the [NHibernate.Spatial Mailing List](https://groups.google.com/forum/#!forum/nhibernate-spatial). diff --git a/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Data/general/TestSimple.xml b/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Data/general/TestSimple.xml new file mode 100644 index 0000000..f2d3c45 --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Data/general/TestSimple.xml @@ -0,0 +1,294 @@ + + + + + P - point + + POINT(10 10) + + + + true + + + + + + mP - multipoint with repeated points + + MULTIPOINT (80 280, 80 220, 160 220, 80 220) + + + + false + + + + + + mP - multipoint with no repeated points + + MULTIPOINT (80 280, 80 220, 160 220) + + + + true + + + + + + mP - empty + + MULTIPOINT EMPTY + + + + false + + + + + + L - simple line + + LINESTRING(10 10, 20 20) + + + + true + + + + + + L - non-simple, proper interior intersection + + LINESTRING (20 60, 160 60, 80 160, 80 20) + + + + false + + + + + + L - non-simple, interior intersection at vertices + + LINESTRING (20 80, 80 20, 80 80, 140 60, 80 20, 160 20) + + + + false + + + + + + L - non-simple, interior intersection at Bdy/non-vertex + + LINESTRING (20 60, 100 60, 60 100, 60 60) + + + + false + + + + + + L - non-simple, interior intersection at Bdy/vertex + + LINESTRING (20 60, 60 60, 100 60, 60 100, 60 60) + + + + false + + + + + + L - simple, intersection at Bdy/Bdy (ring) + + LINESTRING (20 20, 80 20, 80 80, 20 20) + + + + true + + + + + + L - simple, intersection at Bdy/Bdy + non-vertex + + LINESTRING (80 80, 20 20, 20 80, 140 80, 140 140, 80 80) + + + + false + + + + + + L - empty + + LINESTRING EMPTY + + + + false + + + + + + mL - intersection between elements at non-vertex + + MULTILINESTRING( + (40 140, 160 40), + (160 140, 40 40)) + + + + false + + + + + + mL - no intersection between elements + + MULTILINESTRING( + (20 160, 20 20), + (100 160, 100 20)) + + + + true + + + + + + mL - mutual intersection at endpoints only + + MULTILINESTRING ((60 140, 20 80, 60 40), + (60 40, 100 80, 60 140)) + + + + true + + + + + + mL - one element is non-simple + + MULTILINESTRING ((60 40, 140 40, 100 120, 100 0), + (100 200, 200 120)) + + + + false + + + + + + mL - proper intersection between elements at vertex + + MULTILINESTRING ((40 120, 100 60), + (160 120, 100 60), + (40 60, 160 60)) + + + + false + + + + + + mL - intersection between closed lines + + MULTILINESTRING ((80 160, 40 220, 40 100, 80 160), + (80 160, 120 220, 120 100, 80 160)) + + + + false + + + + + + mL - intersection between closed and open lines + + MULTILINESTRING ((80 160, 40 220), + (80 160, 120 220, 120 100, 80 160), + (40 100, 80 160)) + + + + false + + + + + + A + + POLYGON ((180 260, 80 300, 40 180, 160 120, 180 260)) + + + + true + + + + + + A - empty + + POLYGON EMPTY + + + + false + + + + + + mA + + MULTIPOLYGON (((240 160, 140 220, 80 60, 220 40, 240 160)), + ((160 380, 100 240, 20 380, 160 380), + (120 340, 60 360, 80 320, 120 340))) + + + + true + + + + + + mA - with touching elements + + MULTIPOLYGON (((240 160, 100 240, 80 60, 220 40, 240 160)), + ((160 380, 100 240, 20 380, 160 380), + (120 340, 60 360, 80 320, 120 340))) + + + + true + + + + diff --git a/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Data/general/TestValid.xml b/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Data/general/TestValid.xml new file mode 100644 index 0000000..c7098d9 --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Data/general/TestValid.xml @@ -0,0 +1,678 @@ + + + + + L - linear-ring bowtie + LINEARRING(0 0, 100 100, 100 0, 0 100, 0 0) + + false + + + + + L - linestring bowtie + LINESTRING(0 0, 100 100, 100 0, 0 100, 0 0) + + true + + + + + P - point + + POINT(10 10) + + + + true + + + + + + mP - no repeated points + + MULTIPOINT(10 10, 20 20, 30 30) + + + true + + + + + P - repeated points + + MULTIPOINT(10 10, 20 20, 30 30, 10 10) + + + true + + + + + L - no repeated points + + LINESTRING (40 180, 120 120, 140 200, 200 140, 240 200) + + + true + + + + + L - repeated points + + LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200) + + + true + + + + + L - linestring with two identical points + LINESTRING(0 0, 0 0) + + false + + + + + A - zero-area polygon + POLYGON ((0 0, 0 0, 0 0, 0 0, 0 0)) + + false + + + + + A - polygon with too few points + POLYGON ((0 0, 10 0, 20 0, 0 0, 0 0)) + + false + + + + + A - polygon with repeated point + POLYGON ((107 246, 107 246, 250 285, 294 137, 151 90, 15 125, 157 174, 107 246)) + + true + + + + + A - polygon with degenerate hole ring (A-B-A) + + POLYGON ((0 0, 0 240, 260 240, 260 0, 0 0), + (220 200, 40 200, 40 20, 40 200, 220 200, 220 200)) + + + false + + + + + mA - multipolygon with component with too few points + + MULTIPOLYGON ( ((100 20, 180 20, 180 100, 100 100, 100 20)), + ((20 100, 100 100, 100 180, 20 180, 20 100)), + ((100 180, 180 180, 180 260, 100 260, 100 180)), + ((180 100, 180 180, 180 180, 180 100))) + + + false + + + + + A - polygon self-intersects at non-vertex + POLYGON ((0 40, 0 0, 40 40, 40 0, 0 40)) + + false + + + + A - polygon self-intersects at vertex + MULTIPOLYGON ( ((0 40, 20 20, 40 0, 40 40, 20 20, 0 0, 0 40)) ) + + false + + + + A - polygon self-intersects at vertex/non-vertex + POLYGON ((0 40, 20 20, 40 0, 40 40, 0 0, 0 40)) + + false + + + + A - hole self-intersects at non-vertex + POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 0 0, 40 40, 40 0, 0 40)) + + false + + + + A - polygon self-intersects at vertex + POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 20 20, 0 0, 0 40)) + + false + + + + A - polygon self-intersects at vertex/non-vertex + POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 0 0, 0 40)) + + false + + + + A - Valid doughnut + POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40)) + + true + + + + A - shell has repeated points + POLYGON ((0 60, 0 0, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40)) + + true + + + + + A - shell touches hole without crossing it (valid) + POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 60 20, 20 40)) + + true + + + + A - shell touches hole without crossing it, but does so twice (invalid) + POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (0 40, 20 20, 60 20, 0 40)) + + false + + + + A - hole touches hole without crossing it (valid) + POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 20 100)) + + true + + + + A - holel touches hole without crossing it, but does so twice (invalid) + + POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), + (100 100, 100 20, 120 20, 120 100, 100 100), + (20 100, 20 40, 100 40, 80 60, 100 80, 20 100)) + + + false + + + + A - hole touches hole without crossing it, but does so at an infinite number of points (invalid) + + POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), + (100 100, 100 20, 120 20, 120 100, 100 100), + (20 100, 20 40, 100 40, 100 80, 20 100)) + + + false + + + + A - spike (invalid) + POLYGON ((0 60, 0 0, 60 0, 60 20, 100 20, 60 20, 60 60, 0 60)) + + false + + + + A - puncture (invalid) + POLYGON ((0 60, 0 0, 60 0, 60 20, 20 20, 60 20, 60 60, 0 60)) + + false + + + + A - hole within a hole (invalid) + POLYGON ((0 140, 0 0, 180 0, 180 140, 0 140), (20 20, 160 20, 160 120, 20 120, 20 20), (40 100, 40 40, 140 40, 140 100, 40 100)) + + false + + + + A - empty shell and holes (valid) + POLYGON (EMPTY, EMPTY, EMPTY) + + false + + + + + A - hole overlapping shell at non-vertex + + POLYGON ((60 280, 260 180, 60 80, 60 280), + (140 80, 120 180, 200 180, 140 80)) + + + + false + + + + + + A - shell self-overlaps + + POLYGON ((60 340, 60 100, 340 100, 340 280, 340 200, 340 340, 60 340)) + + + false + + + + + A - hole with repeated points + + POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), + (70 230, 80 230, 80 220, 80 220, 70 230)) + + + + true + + + + + A - hole outside but adjacent to shell + + POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), + (180 160, 240 60, 120 60, 180 160)) + + + + false + + + + + + A - hole touches shell at two points + + POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), + (140 180, 40 180, 140 260, 140 180)) + + + + false + + + + + + A - hole touches shell at one non-vertex point + + POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), + (140 180, 40 180, 140 240, 140 180)) + + + true + + + + + A - hole touches shell at one vertex point + + POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), + (140 180, 40 260, 140 240, 140 180)) + + + true + + + + + A - hole outside shell + + POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), + (160 120, 180 100, 160 80, 160 120)) + + + + false + + + + + + A - hole identical to shell + + POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), + (20 180, 20 20, 140 20, 140 180, 20 180)) + + + + false + + + + + + A - hole identical to shell + + POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), + (20 180, 20 20, 140 20, 140 180, 20 180)) + + + + false + + + + + + A - hole self-intersects + + POLYGON ((380 340, 40 340, 40 20, 380 20, 380 340), + (120 300, 300 280, 320 200, 160 140, 200 80, 320 120, 320 200, 360 60, 120 40, 120 300)) + + + false + + + + + A - holes overlap, first point is identical + + POLYGON ((20 320, 260 320, 260 20, 20 20, 20 320), + (140 280, 80 100, 200 100, 140 280), + (140 280, 40 80, 240 80, 140 280)) + + + false + + + + + A - holes do not overlap, first point is identical + + POLYGON ((20 320, 240 320, 240 40, 20 40, 20 320), + (140 180, 60 120, 60 240, 140 180), + (140 180, 200 120, 200 240, 140 180)) + + + true + + + + + A - shell self-touches at vertex + + POLYGON ((340 320, 340 200, 200 280, 200 80, 340 200, 340 20, 60 20, 60 340, 340 320)) + + + false + + + + + A - shell self-touches at non-vertex + + POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320, 300 320)) + + + false + + + + + A - chain of holes surrounds an island inside the polygon + + POLYGON ((40 300, 40 20, 280 20, 280 300, 40 300), + (120 240, 80 180, 160 220, 120 240), + (220 240, 160 220, 220 160, 220 240), + (160 100, 80 180, 100 80, 160 100), + (160 100, 220 160, 240 100, 160 100)) + + + false + + + + + A - chain of holes splits polygon in two (touching at vertices) + + POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), + (100 120, 40 20, 180 100, 100 120), + (200 200, 180 100, 240 160, 200 200), + (260 260, 240 160, 300 200, 260 260), + (300 300, 300 200, 340 320, 300 300)) + + + false + + + + + A - chain of holes splits polygon in two (touching at non-vertex) + + POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), + (100 120, 40 20, 180 100, 100 120), + (200 200, 180 100, 240 160, 200 200), + (260 260, 240 160, 300 200, 260 260), + (300 300, 300 200, 340 260, 300 300)) + + + false + + + + + A - holes touch in one point + + POLYGON ((190 190, 360 20, 20 20, 190 190), + (90 50, 150 110, 190 50, 90 50), + (190 50, 230 110, 290 50, 190 50)) + + + true + + + + + A - holes touch in one point + + POLYGON ((190 190, 360 20, 20 20, 190 190), + (90 50, 150 110, 190 50, 90 50), + (190 50, 230 110, 290 50, 190 50)) + + + true + + + + + A - hole disconnects interiors + + POLYGON ((0 0, 10 10, 10 0, 0 0), + (5 5, 5 0, 10 5, 5 5)) + + + false + + + + + A - touching holes do NOT disconnect (isCCW bug) + + POLYGON ((60 40, 60 240, 460 240, 460 40, 60 40), + (260 200, 340 60, 400 120, 260 200), + (260 200, 120 100, 200 60, 260 200)) + + + true + + + + + mA - adjacent shells (shared vertices) + + MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), + ((140 120, 40 120, 40 200, 140 200, 140 120))) + + + + false + + + + + + mA - adjacent shells (different vertices) + + MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), + ((160 120, 60 120, 40 200, 140 200, 160 120))) + + + + false + + + + + + mA - nested overlapping shells + + MULTIPOLYGON (((80 260, 240 260, 240 100, 80 100, 80 260)), + ((120 240, 220 240, 220 140, 120 140, 120 240))) + + + false + + + + + mA - nested non-overlapping shells + + MULTIPOLYGON (((60 320, 60 80, 300 80, 60 320), + (80 280, 80 100, 260 100, 80 280)), + ((120 160, 140 160, 140 140, 120 160))) + + + true + + + + + mA - nested non-overlapping shells, all vertices touch + + MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), + (220 340, 180 240, 60 200, 180 160, 340 60, 240 220, 220 340)), + ((180 240, 180 160, 240 220, 180 240))) + + + true + + + + + mA - nested overlapping shells, all vertices touch + + MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), + (220 340, 180 240, 60 200, 140 100, 340 60, 300 240, 220 340)), + ((60 200, 340 60, 220 340, 60 200))) + + + false + + + + + mA - nested non-overlapping shells, all vertices touch + + MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), + (220 340, 80 320, 60 200, 140 100, 340 60, 300 240, 220 340)), + ((60 200, 340 60, 220 340, 60 200))) + + + true + + + + + mA - nested overlapping shells, all vertices touch + + MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), + (220 340, 180 240, 60 200, 200 180, 340 60, 240 220, 220 340)), + ((60 200, 340 60, 220 340, 60 200))) + + + false + + + + + mA - disconnected exterior + + MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), + ((20 100, 100 100, 100 180, 20 180, 20 100)), + ((100 180, 180 180, 180 260, 100 260, 100 180)), + ((180 100, 260 100, 260 180, 180 180, 180 100))) + + + true + + + + + mA - shells touch in single point + + MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110)), + ((110 110, 150 20, 70 20, 110 110))) + + + true + + + + + mA - duplicate shells + + MULTIPOLYGON (((60 300, 320 220, 260 60, 60 100, 60 300)), + ((60 300, 320 220, 260 60, 60 100, 60 300))) + + + false + + + + + mA - shells are not nested but share all vertices + + MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), + ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80))) + + + true + + + + + mA - shell is nested inside first hole + + MULTIPOLYGON (((0 0, 0 8, 8 8, 8 0, 0 0), + (3 3, 7 3, 7 7, 3 7, 3 3), + (1 1, 2 1, 2 2, 1 2, 1 1)), + ((4 4, 4 6, 6 6, 6 4, 4 4))) + + + true + + + diff --git a/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Model/NtsTestCase.cs b/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Model/NtsTestCase.cs new file mode 100644 index 0000000..f5b4c68 --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Model/NtsTestCase.cs @@ -0,0 +1,73 @@ +using NetTopologySuite.Geometries; +using System; + +namespace Tests.NHibernate.Spatial.NtsTestCases.Model +{ + [Serializable] + public class NtsTestCase + { + private long id; + + private string description; + + private Geometry geometryA = GeometryCollection.Empty; + + private Geometry geometryB = GeometryCollection.Empty; + + private string operation; + + private string parameter; + + private Geometry geometryResult = GeometryCollection.Empty; + + private bool booleanResult; + + public virtual long Id + { + get => id; + set => id = value; + } + + public virtual string Description + { + get => description; + set => description = value; + } + + public virtual Geometry GeometryA + { + get => geometryA; + set => geometryA = value; + } + + public virtual Geometry GeometryB + { + get => geometryB; + set => geometryB = value; + } + + public virtual string Operation + { + get => operation; + set => operation = value; + } + + public virtual string Parameter + { + get => parameter; + set => parameter = value; + } + + public virtual Geometry GeometryResult + { + get => geometryResult; + set => geometryResult = value; + } + + public virtual bool BooleanResult + { + get => booleanResult; + set => booleanResult = value; + } + } +} diff --git a/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Model/NtsTestCase.hbm.xml b/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Model/NtsTestCase.hbm.xml new file mode 100644 index 0000000..ee587a0 --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Model/NtsTestCase.hbm.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + -1 + GEOMETRY + + + + + -1 + GEOMETRY + + + + + + + -1 + GEOMETRY + + + + + diff --git a/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteConformanceItemsFixture.cs b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteConformanceItemsFixture.cs new file mode 100644 index 0000000..71f293a --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteConformanceItemsFixture.cs @@ -0,0 +1,112 @@ +using System.Linq; +using NetTopologySuite.Geometries; +using NHibernate.Cfg; +using NHibernate.Spatial.Metadata; +using NUnit.Framework; +using Tests.NHibernate.Spatial.OgcSfSql11Compliance; +using Tests.NHibernate.Spatial.OgcSfSql11Compliance.Model; + +namespace Tests.NHibernate.Spatial +{ + [TestFixture] + public class SpatiaLiteConformanceItemsFixture : ConformanceItemsFixture + { + [Test] + public override void ConformanceItemT05Hql() + { + if (!Metadata.SupportsSpatialMetadata(session, MetadataClass.SpatialReferenceSystem)) + { + Assert.Ignore("Provider does not support spatial metadata"); + } + var srs = session.CreateQuery( + "from SpatiaLiteSpatialReferenceSystem where SRID=101") + .UniqueResult(); + + Assert.IsNotNull(srs); + Assert.AreEqual(SpatialReferenceSystemWKT, srs.WellKnownText); + + // Alternative syntax for identifiers: + srs = session.CreateQuery( + "from SpatiaLiteSpatialReferenceSystem s where s=101") + .UniqueResult(); + + Assert.IsNotNull(srs); + Assert.AreEqual(SpatialReferenceSystemWKT, srs.WellKnownText); + } + + /// + /// Overridden because GetPointN is not zero-based in SpatiaLite + /// + [Test] + public override void ConformanceItemT23Linq() + { + var query = + from t in session.Query() + where t.Fid == 102 + select ((LineString) t.Centerline) + .GetPointN(1); + + Geometry geometry = query.Single(); + var expected = Wkt.Read("POINT( 0 18 )"); + + Assert.IsTrue(expected.EqualsTopologically(geometry)); + } + + /// + /// Overridden because GetPointN is not zero-based in SpatiaLite + /// + [Test] + public override void ConformanceItemT29Linq() + { + var query = + from t in session.Query() + where t.Name == "Blue Lake" + select ((Polygon) t.Shore).GetInteriorRingN(1); + + Geometry geometry = query.Single(); + var expected = Wkt.Read("LINESTRING(59 18, 67 18, 67 13, 59 13, 59 18)"); + + Assert.IsTrue(expected.EqualsTopologically(geometry)); + } + + /// + /// Overridden because GetPointN is not zero-based in SpatiaLite + /// + [Test] + public override void ConformanceItemT31Linq() + { + var query = + from t in session.Query() + where t.Name == "Route 75" + select t.Centerlines.GetGeometryN(2); + + var geometry = query.Single(); + var expected = Wkt.Read("LINESTRING( 16 0, 16 23, 16 48 )"); + + Assert.IsTrue(expected.EqualsTopologically(geometry)); + } + + protected override void Configure(Configuration configuration) + { + TestConfiguration.Configure(configuration); + } + + protected override void OnBeforeCreateSchema() + { + using (var session = sessions.OpenSession()) + { + session.Save(new SpatiaLiteSpatialReferenceSystem(101, "POSC", 32214, "NHSP", "PROJ.4", SpatialReferenceSystemWKT)); + session.Flush(); + } + } + + protected override void OnAfterDropSchema() + { + using (var session = sessions.OpenSession()) + { + session.Delete("from SpatiaLiteSpatialReferenceSystem where SRID=101"); + session.Flush(); + } + } + } +} diff --git a/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteCriteriaFixture.cs b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteCriteriaFixture.cs new file mode 100644 index 0000000..192211c --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteCriteriaFixture.cs @@ -0,0 +1,14 @@ +using NHibernate.Cfg; +using NUnit.Framework; + +namespace Tests.NHibernate.Spatial +{ + [TestFixture] + public class SpatiaLiteCriteriaFixture : CriteriaFixture + { + protected override void Configure(Configuration configuration) + { + TestConfiguration.Configure(configuration); + } + } +} diff --git a/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteNtsTestCasesFixture.cs b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteNtsTestCasesFixture.cs new file mode 100644 index 0000000..82bf112 --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteNtsTestCasesFixture.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using NHibernate.Cfg; +using NUnit.Framework; +using Tests.NHibernate.Spatial.NtsTestCases; +using Tests.NHibernate.Spatial.NtsTestCases.Model; + +namespace Tests.NHibernate.Spatial +{ + [TestFixture] + public class SpatiaLiteNtsTestCasesFixture : NtsTestCasesFixture + { + private const string LocalDataPath = "../../../../Tests.NHibernate.Spatial.SpatiaLite/NtsTestCases/Data"; + + protected override string TestSimpleDataPath => Path.Combine(LocalDataPath, "general", "TestSimple.xml"); + + protected override string TestValidDataPath => Path.Combine(LocalDataPath, "general", "TestValid.xml"); + + protected override Type[] Mappings + { + get + { + return new[] + { +#pragma warning disable 0436 + typeof(NtsTestCase) +#pragma warning restore 0436 + }; + } + } + + [Test] + [Ignore("Not supported by SpatiaLite")] + public override void EqualsExact() + { } + + [Test] + [Ignore("Not supported by SpatiaLite")] + public override void StringRelate() + { } + + protected override void Configure(Configuration configuration) + { + TestConfiguration.Configure(configuration); + } + } +} diff --git a/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteProjectionsFixture.cs b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteProjectionsFixture.cs new file mode 100644 index 0000000..d13d7bd --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteProjectionsFixture.cs @@ -0,0 +1,19 @@ +using NHibernate.Cfg; +using NUnit.Framework; + +namespace Tests.NHibernate.Spatial +{ + [TestFixture] + public class SpatiaLiteProjectionsFixture : ProjectionsFixture + { + [Test] + [Ignore("SpatiaLite does not support Intersection spatial aggregate function")] + public override void IntersectionAll() + { } + + protected override void Configure(Configuration config) + { + TestConfiguration.Configure(config); + } + } +} diff --git a/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteSpatialQueriesFixture.cs b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteSpatialQueriesFixture.cs new file mode 100644 index 0000000..e7b9c48 --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/SpatiaLiteSpatialQueriesFixture.cs @@ -0,0 +1,96 @@ +using NHibernate; +using NHibernate.Cfg; +using NUnit.Framework; +using Tests.NHibernate.Spatial.RandomGeometries; + +namespace Tests.NHibernate.Spatial +{ + [TestFixture] + public class SpatiaLiteSpatialQueriesFixture : SpatialQueriesFixture + { + protected override void Configure(Configuration configuration) + { + TestConfiguration.Configure(configuration); + } + + protected override string SqlLineStringFilter(string filterString) + { + return $@" + SELECT count(*) + FROM linestringtest + WHERE MbrIntersects(the_geom, ST_GeomFromText('{filterString}', 4326)) + "; + } + + protected override string SqlPolygonFilter(string filterString) + { + return $@" + SELECT count(*) + FROM polygontest + WHERE MbrIntersects(the_geom, ST_GeomFromText('{filterString}', 4326)) + "; + } + + protected override string SqlMultiLineStringFilter(string filterString) + { + return $@" + SELECT count(*) + FROM multilinestringtest + WHERE MbrIntersects(the_geom, ST_GeomFromText('{filterString}', 4326)) + "; + } + + protected override string SqlOvelapsLineString(string filterString) + { + return $@" + SELECT count(*) + FROM linestringtest + WHERE the_geom IS NOT NULL + AND ST_Overlaps(ST_PolygonFromText('{filterString}', 4326), the_geom) + "; + } + + protected override string SqlIntersectsLineString(string filterString) + { + return $@" + SELECT count(*) + FROM linestringtest + WHERE the_geom IS NOT NULL + AND ST_Intersects(ST_PolygonFromText('{filterString}', 4326), the_geom) + "; + } + + protected override ISQLQuery SqlIsEmptyLineString(ISession session) + { + return session.CreateSQLQuery(@" + SELECT ST_IsEmpty(the_geom) as result + FROM linestringtest + WHERE oid = ? + AND the_geom IS NOT NULL + ") + .AddScalar("result", NHibernateUtil.Boolean); + } + + protected override ISQLQuery SqlIsSimpleLineString(ISession session) + { + return session.CreateSQLQuery(@" + SELECT ST_IsSimple(the_geom) as result + FROM linestringtest + WHERE oid = ? + AND the_geom IS NOT NULL + ") + .AddScalar("result", NHibernateUtil.Boolean); + } + + protected override ISQLQuery SqlAsBinaryLineString(ISession session) + { + return session.CreateSQLQuery(@" + SELECT ST_AsBinary(the_geom) as result + FROM linestringtest + WHERE oid = ? + AND the_geom IS NOT NULL + ") + .AddScalar("result", NHibernateUtil.BinaryBlob); + } + } +} diff --git a/Tests.NHibernate.Spatial.SpatiaLite/TestConfiguration.cs b/Tests.NHibernate.Spatial.SpatiaLite/TestConfiguration.cs new file mode 100644 index 0000000..1ee83ba --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/TestConfiguration.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.Configuration; +using NHibernate.Bytecode; +using NHibernate.Cfg; +using NHibernate.Spatial.Dialect; +using NHibernate.Spatial.Driver; +using System.Collections.Generic; +using System.IO; + +namespace Tests.NHibernate.Spatial +{ + internal static class TestConfiguration + { + private static readonly IConfigurationRoot _configurationRoot; + + static TestConfiguration() + { + _configurationRoot = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + + try + { + File.Delete("nhsp_test.sqlite"); + } + catch + { } + } + + public static void Configure(Configuration configuration) + { + IDictionary properties = new Dictionary + { + [Environment.ProxyFactoryFactoryClass] = typeof(StaticProxyFactoryFactory).AssemblyQualifiedName, + [Environment.Dialect] = typeof(SpatiaLiteDialect).AssemblyQualifiedName, + [Environment.ConnectionProvider] = typeof(DebugConnectionProvider).AssemblyQualifiedName, + [Environment.ConnectionDriver] = typeof(SpatiaLiteDriver).AssemblyQualifiedName, + [Environment.ConnectionString] = _configurationRoot.GetConnectionString("SpatiaLite") + }; + configuration.SetProperties(properties); + } + } +} diff --git a/Tests.NHibernate.Spatial.SpatiaLite/Tests.NHibernate.Spatial.SpatiaLite.csproj b/Tests.NHibernate.Spatial.SpatiaLite/Tests.NHibernate.Spatial.SpatiaLite.csproj new file mode 100644 index 0000000..a5480b9 --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/Tests.NHibernate.Spatial.SpatiaLite.csproj @@ -0,0 +1,35 @@ + + + + net6.0 + + false + + Tests.NHibernate.Spatial + + + + + + + + + + + + + + + + + + + + Always + + + PreserveNewest + + + + diff --git a/Tests.NHibernate.Spatial.SpatiaLite/appsettings.json b/Tests.NHibernate.Spatial.SpatiaLite/appsettings.json new file mode 100644 index 0000000..e49266d --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "SpatiaLite": "Data Source=nhsp_test.sqlite;Version=3;" + } +} diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/README.md b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/README.md new file mode 100644 index 0000000..8a4c789 --- /dev/null +++ b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/README.md @@ -0,0 +1,6 @@ +# SpatiaLite 5.1.0 + +http://www.gaia-gis.it/gaia-sins/windows-bin-amd64 + +> NB - These DLLs are only used for local testing on Windows. For testing on Linux (either locally + or as part of the CI build), the `libsqlite3-mod-spatialite` package should be installed. diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libcrypto-3-x64.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libcrypto-3-x64.dll new file mode 100644 index 0000000..ba5f002 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libcrypto-3-x64.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libcurl-4.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libcurl-4.dll new file mode 100644 index 0000000..76aea45 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libcurl-4.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libexpat-1.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libexpat-1.dll new file mode 100644 index 0000000..3b7d9e6 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libexpat-1.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libfreexl-1.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libfreexl-1.dll new file mode 100644 index 0000000..ce39a3e Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libfreexl-1.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgcc_s_seh-1.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgcc_s_seh-1.dll new file mode 100644 index 0000000..fb2e767 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgcc_s_seh-1.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgeos.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgeos.dll new file mode 100644 index 0000000..d841298 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgeos.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgeos_c.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgeos_c.dll new file mode 100644 index 0000000..ccad769 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libgeos_c.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libiconv-2.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libiconv-2.dll new file mode 100644 index 0000000..b20e68c Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libiconv-2.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libjpeg-62.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libjpeg-62.dll new file mode 100644 index 0000000..0001d81 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libjpeg-62.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/liblzma-5.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/liblzma-5.dll new file mode 100644 index 0000000..713a108 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/liblzma-5.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libminizip-1.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libminizip-1.dll new file mode 100644 index 0000000..94e0c66 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libminizip-1.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libproj_9_2.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libproj_9_2.dll new file mode 100644 index 0000000..92133fd Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libproj_9_2.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/librttopo-1.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/librttopo-1.dll new file mode 100644 index 0000000..7985d62 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/librttopo-1.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libsharpyuv-0.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libsharpyuv-0.dll new file mode 100644 index 0000000..4f23b5b Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libsharpyuv-0.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libsqlite3-0.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libsqlite3-0.dll new file mode 100644 index 0000000..76f4bcf Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libsqlite3-0.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libssl-3-x64.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libssl-3-x64.dll new file mode 100644 index 0000000..0d19a69 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libssl-3-x64.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libstdc++-6.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libstdc++-6.dll new file mode 100644 index 0000000..2b2a3e9 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libstdc++-6.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libtiff-6.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libtiff-6.dll new file mode 100644 index 0000000..5f3b936 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libtiff-6.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libwebp-7.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libwebp-7.dll new file mode 100644 index 0000000..308a716 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libwebp-7.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libwinpthread-1.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libwinpthread-1.dll new file mode 100644 index 0000000..c47b8da Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libwinpthread-1.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libxml2.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libxml2.dll new file mode 100644 index 0000000..99d2792 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libxml2.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libzstd.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libzstd.dll new file mode 100644 index 0000000..d2020f1 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/libzstd.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/mod_spatialite.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/mod_spatialite.dll new file mode 100644 index 0000000..6b64fb9 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/mod_spatialite.dll differ diff --git a/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/zlib1.dll b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/zlib1.dll new file mode 100644 index 0000000..fccdbc6 Binary files /dev/null and b/Tests.NHibernate.Spatial.SpatiaLite/lib/spatialite/zlib1.dll differ diff --git a/Tests.NHibernate.Spatial/OgcSfSql11Compliance/ConformanceItemsFixture.cs b/Tests.NHibernate.Spatial/OgcSfSql11Compliance/ConformanceItemsFixture.cs index 27db22a..224e8c0 100644 --- a/Tests.NHibernate.Spatial/OgcSfSql11Compliance/ConformanceItemsFixture.cs +++ b/Tests.NHibernate.Spatial/OgcSfSql11Compliance/ConformanceItemsFixture.cs @@ -410,7 +410,7 @@ from g in session.Query() /// /// [Test] - public void ConformanceItemT05Hql() + public virtual void ConformanceItemT05Hql() { if (!Metadata.SupportsSpatialMetadata(session, MetadataClass.SpatialReferenceSystem)) {