Skip to content

Commit b41d5a1

Browse files
committed
Add support for NetTopologySuite spatial library
1 parent 0d971da commit b41d5a1

File tree

90 files changed

+7984
-65
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+7984
-65
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OData", "src\Microsoft.AspNetCore.OData\Microsoft.AspNetCore.OData.csproj", "{79D9A62B-36EB-4345-AFCE-081397C29F7E}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OData.NetTopologySuite", "src\Microsoft.AspNetCore.OData.NetTopologySuite\Microsoft.AspNetCore.OData.NetTopologySuite.csproj", "{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}"
11+
EndProject
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0C88DD14-F956-CE84-757C-A364CCF449FC}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OData.NetTopologySuite.Tests", "test\Microsoft.AspNetCore.OData.NetTopologySuite.Tests\Microsoft.AspNetCore.OData.NetTopologySuite.Tests.csproj", "{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}"
15+
EndProject
16+
Global
17+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
18+
Debug|Any CPU = Debug|Any CPU
19+
Debug|x64 = Debug|x64
20+
Debug|x86 = Debug|x86
21+
Release|Any CPU = Release|Any CPU
22+
Release|x64 = Release|x64
23+
Release|x86 = Release|x86
24+
EndGlobalSection
25+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
26+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|x64.ActiveCfg = Debug|Any CPU
29+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|x64.Build.0 = Debug|Any CPU
30+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|x86.ActiveCfg = Debug|Any CPU
31+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|x86.Build.0 = Debug|Any CPU
32+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
33+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|Any CPU.Build.0 = Release|Any CPU
34+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|x64.ActiveCfg = Release|Any CPU
35+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|x64.Build.0 = Release|Any CPU
36+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|x86.ActiveCfg = Release|Any CPU
37+
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|x86.Build.0 = Release|Any CPU
38+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|x64.ActiveCfg = Debug|Any CPU
41+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|x64.Build.0 = Debug|Any CPU
42+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|x86.ActiveCfg = Debug|Any CPU
43+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|x86.Build.0 = Debug|Any CPU
44+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|Any CPU.ActiveCfg = Release|Any CPU
45+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|Any CPU.Build.0 = Release|Any CPU
46+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|x64.ActiveCfg = Release|Any CPU
47+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|x64.Build.0 = Release|Any CPU
48+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|x86.ActiveCfg = Release|Any CPU
49+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|x86.Build.0 = Release|Any CPU
50+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
52+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|x64.ActiveCfg = Debug|Any CPU
53+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|x64.Build.0 = Debug|Any CPU
54+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|x86.ActiveCfg = Debug|Any CPU
55+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|x86.Build.0 = Debug|Any CPU
56+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
57+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|Any CPU.Build.0 = Release|Any CPU
58+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|x64.ActiveCfg = Release|Any CPU
59+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|x64.Build.0 = Release|Any CPU
60+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|x86.ActiveCfg = Release|Any CPU
61+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|x86.Build.0 = Release|Any CPU
62+
EndGlobalSection
63+
GlobalSection(SolutionProperties) = preSolution
64+
HideSolutionNode = FALSE
65+
EndGlobalSection
66+
GlobalSection(NestedProjects) = preSolution
67+
{79D9A62B-36EB-4345-AFCE-081397C29F7E} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
68+
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
69+
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8} = {0C88DD14-F956-CE84-757C-A364CCF449FC}
70+
EndGlobalSection
71+
EndGlobal
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package>
3+
<metadata>
4+
<id>Microsoft.AspNetCore.OData.NetTopologySuite</id>
5+
<title>ASP.NET Core OData NetTopologySuite Integration</title>
6+
<version>$VersionFullSemantic$-Nightly$NightlyBuildVersion$</version>
7+
<authors>Microsoft</authors>
8+
<copyright>&#169; Microsoft Corporation. All rights reserved.</copyright>
9+
<description>Adds NetTopologySuite (NTS) spatial support to ASP.NET Core OData. Enables using NTS geometry types (Point, LineString, Polygon, Multi*, GeometryCollection) as Edm.Geometry, and opt-in Edm.Geography mapping via the [Geography] attribute and conventions. Includes an EDM type-mapping provider and spatial serializers/deserializers.</description>
10+
<summary>NetTopologySuite spatial support for ASP.NET Core OData.</summary>
11+
<language>en-US</language>
12+
<projectUrl>http://github.com/OData/AspNetCoreOData</projectUrl>
13+
<license type="expression">MIT</license>
14+
<requireLicenseAcceptance>true</requireLicenseAcceptance>
15+
<tags>Microsoft AspNetCore WebApi OData</tags>
16+
<readme>docs\README.md</readme>
17+
<icon>images\odata.png</icon>
18+
<repository type="git" url="https://github.com/OData/AspNetCoreOData.git" branch="master" />
19+
<dependencies>
20+
<group targetFramework=".net10.0">
21+
<dependency id="Microsoft.AspNetCore.OData" version="$ODataAspNetCorePackageDependency$" />
22+
<dependency id="Microsoft.OData.ModelBuilder" version="$ODataModelBuilderPackageDependency$" />
23+
<dependency id="Microsoft.OData.Core" version="$ODataLibPackageDependency$" />
24+
<dependency id="Microsoft.OData.Edm" version="$ODataLibPackageDependency$" />
25+
<dependency id="Microsoft.Spatial" version="$ODataLibPackageDependency$" />
26+
</group>
27+
</dependencies>
28+
</metadata>
29+
<files>
30+
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.dll" target="lib\net10.0" />
31+
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.xml" target="lib\net10.0" />
32+
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.pdb" target="lib\net10.0" />
33+
<file src="$SourcesRoot$\images\odata.png" target="images\" />
34+
35+
<!-- readme file -->
36+
<file src="$SourcesRoot$\README.md" target="docs\" />
37+
</files>
38+
</package>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package>
3+
<metadata>
4+
<id>Microsoft.AspNetCore.OData.NetTopologySuite</id>
5+
<title>ASP.NET Core OData NetTopologySuite Integration</title>
6+
<version>$VersionNuGetSemantic$</version>
7+
<authors>Microsoft</authors>
8+
<copyright>&#169; Microsoft Corporation. All rights reserved.</copyright>
9+
<description>Adds NetTopologySuite (NTS) spatial support to ASP.NET Core OData. Enables using NTS geometry types (Point, LineString, Polygon, Multi*, GeometryCollection) as Edm.Geometry, and opt-in Edm.Geography mapping via the [Geography] attribute and conventions. Includes an EDM type-mapping provider and spatial serializers/deserializers.</description>
10+
<summary>NetTopologySuite spatial support for ASP.NET Core OData.</summary>
11+
<language>en-US</language>
12+
<projectUrl>http://github.com/OData/AspNetCoreOData</projectUrl>
13+
<license type="expression">MIT</license>
14+
<requireLicenseAcceptance>true</requireLicenseAcceptance>
15+
<tags>Microsoft AspNetCore OData NetTopologySuite</tags>
16+
<readme>docs\README.md</readme>
17+
<icon>images\odata.png</icon>
18+
<repository type="git" url="https://github.com/OData/AspNetCoreOData.git" branch="master" />
19+
<dependencies>
20+
<group targetFramework=".net10.0">
21+
<dependency id="Microsoft.AspNetCore.OData" version="$ODataAspNetCorePackageDependency$" />
22+
<dependency id="Microsoft.OData.ModelBuilder" version="$ODataModelBuilderPackageDependency$" />
23+
<dependency id="Microsoft.OData.Core" version="$ODataLibPackageDependency$" />
24+
<dependency id="Microsoft.OData.Edm" version="$ODataLibPackageDependency$" />
25+
<dependency id="Microsoft.Spatial" version="$ODataLibPackageDependency$" />
26+
</group>
27+
</dependencies>
28+
</metadata>
29+
<files>
30+
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.dll" target="lib\net10.0" />
31+
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.xml" target="lib\net10.0" />
32+
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.pdb" target="lib\net10.0" />
33+
<file src="$SourcesRoot$\images\odata.png" target="images\" />
34+
35+
<!-- readme file -->
36+
<file src="$SourcesRoot$\README.md" target="docs\" />
37+
</files>
38+
</package>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//-----------------------------------------------------------------------------
2+
// <copyright file="GeographyAttribute.cs" company=".NET Foundation">
3+
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
4+
// See License.txt in the project root for license information.
5+
// </copyright>
6+
//------------------------------------------------------------------------------
7+
8+
using Microsoft.AspNetCore.OData.NetTopologySuite.Conventions;
9+
using Microsoft.OData.Edm;
10+
using Geometry = NetTopologySuite.Geometries.Geometry;
11+
12+
namespace Microsoft.AspNetCore.OData.NetTopologySuite.Attributes;
13+
14+
/// <summary>
15+
/// Marks a property or CLR type so that NetTopologySuite geometry values are modeled as Edm geography types.
16+
/// </summary>
17+
/// <remarks>
18+
/// - When applied to a property whose CLR type derives from <see cref="Geometry"/>,
19+
/// the property is mapped to the corresponding <see cref="EdmPrimitiveTypeKind"/> in the Edm.Geography* family
20+
/// (for example, Point → GeographyPoint).
21+
/// - When applied to a class, all properties on the type whose CLR types derive from
22+
/// <see cref="Geometry"/> are treated as Edm.Geography*.
23+
/// The behavior is enforced by the conventions
24+
/// <see cref="GeographyAttributeEdmPropertyConvention"/> and
25+
/// <see cref="GeographyAttributeEdmTypeConvention"/>.
26+
/// </remarks>
27+
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
28+
public class GeographyAttribute : Attribute
29+
{
30+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//-----------------------------------------------------------------------------
2+
// <copyright file="Error.cs" company=".NET Foundation">
3+
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
4+
// See License.txt in the project root for license information.
5+
// </copyright>
6+
//------------------------------------------------------------------------------
7+
8+
using System.Diagnostics.CodeAnalysis;
9+
using System.Globalization;
10+
11+
namespace Microsoft.AspNetCore.OData.NetTopologySuite.Common;
12+
13+
[ExcludeFromCodeCoverage]
14+
internal static class Error
15+
{
16+
/// <summary>
17+
/// Formats the specified resource string using <see cref="CultureInfo.CurrentCulture"/>.
18+
/// </summary>
19+
/// <param name="format">A composite format string.</param>
20+
/// <param name="args">An object array that contains zero or more objects to format.</param>
21+
/// <returns>The formatted string.</returns>
22+
internal static string Format(string format, params object[] args)
23+
{
24+
return String.Format(CultureInfo.CurrentCulture, format, args);
25+
}
26+
27+
/// <summary>
28+
/// Creates an <see cref="ArgumentNullException"/> with the provided properties.
29+
/// </summary>
30+
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
31+
/// <returns>The logged <see cref="Exception"/>.</returns>
32+
internal static ArgumentNullException ArgumentNull(string parameterName)
33+
{
34+
return new ArgumentNullException(parameterName);
35+
}
36+
37+
/// <summary>
38+
/// Creates an <see cref="InvalidOperationException"/>.
39+
/// </summary>
40+
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
41+
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
42+
/// <returns>The logged <see cref="Exception"/>.</returns>
43+
internal static InvalidOperationException InvalidOperation(string messageFormat, params object[] messageArgs)
44+
{
45+
return new InvalidOperationException(Error.Format(messageFormat, messageArgs));
46+
}
47+
48+
/// <summary>
49+
/// Creates an <see cref="NotSupportedException"/>.
50+
/// </summary>
51+
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
52+
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
53+
/// <returns>The logged <see cref="Exception"/>.</returns>
54+
internal static NotSupportedException NotSupported(string messageFormat, params object[] messageArgs)
55+
{
56+
return new NotSupportedException(Error.Format(messageFormat, messageArgs));
57+
}
58+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//-----------------------------------------------------------------------------
2+
// <copyright file="GeographyAttributeEdmPropertyConvention.cs" company=".NET Foundation">
3+
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
4+
// See License.txt in the project root for license information.
5+
// </copyright>
6+
//------------------------------------------------------------------------------
7+
8+
using Microsoft.AspNetCore.OData.NetTopologySuite.Attributes;
9+
using Microsoft.AspNetCore.OData.NetTopologySuite.Common;
10+
using Microsoft.AspNetCore.OData.NetTopologySuite.Edm;
11+
using Microsoft.OData.Edm;
12+
using Microsoft.OData.ModelBuilder;
13+
using Microsoft.OData.ModelBuilder.Conventions.Attributes;
14+
using Geometry = NetTopologySuite.Geometries.Geometry;
15+
16+
namespace Microsoft.AspNetCore.OData.NetTopologySuite.Conventions;
17+
18+
/// <summary>
19+
/// A convention that applies to individual properties decorated with the <see cref="GeographyAttribute"/>.
20+
/// If the property’s CLR type is a NetTopologySuite spatial type, it is mapped to the corresponding
21+
/// OData geography Edm type (for example, <c>Point</c> → <c>Edm.GeographyPoint</c>,
22+
/// <c>LineString</c> → <c>Edm.GeographyLineString</c>).
23+
/// </summary>
24+
public class GeographyAttributeEdmPropertyConvention : AttributeEdmPropertyConvention<PropertyConfiguration>
25+
{
26+
/// <summary>
27+
/// Initializes a new instance of the <see cref="GeographyAttributeEdmPropertyConvention"/> class.
28+
/// Configures the convention to match properties annotated with <see cref="GeographyAttribute"/> and
29+
/// to map NetTopologySuite spatial CLR types to their Edm.Geography* primitive kinds.
30+
/// </summary>
31+
public GeographyAttributeEdmPropertyConvention()
32+
: base(attribute => attribute.GetType() == typeof(GeographyAttribute), allowMultiple: false)
33+
{
34+
35+
}
36+
37+
/// <inheritdoc/>
38+
public override void Apply(PropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute, ODataConventionModelBuilder model)
39+
{
40+
if (edmProperty == null)
41+
{
42+
throw Error.ArgumentNull(nameof(edmProperty));
43+
}
44+
45+
if (structuralTypeConfiguration == null)
46+
{
47+
throw Error.ArgumentNull(nameof(structuralTypeConfiguration));
48+
}
49+
50+
if (model == null)
51+
{
52+
throw Error.ArgumentNull(nameof(model));
53+
}
54+
55+
// If the property was added explicitly, we do not apply the convention.
56+
if (edmProperty.AddedExplicitly)
57+
{
58+
return;
59+
}
60+
61+
if (edmProperty is PrimitivePropertyConfiguration primitiveProperty
62+
&& typeof(Geometry).IsAssignableFrom(edmProperty.RelatedClrType))
63+
{
64+
EdmPrimitiveTypeKind? targetPrimitiveTypeKind = EdmSpatialKindMapper.GetGeographyKindForClrType(edmProperty.RelatedClrType);
65+
if (targetPrimitiveTypeKind.HasValue)
66+
{
67+
primitiveProperty.AsSpatial(targetPrimitiveTypeKind.Value);
68+
}
69+
}
70+
else if (edmProperty is CollectionPropertyConfiguration collectionProperty
71+
&& typeof(Geometry).IsAssignableFrom(collectionProperty.RelatedClrType))
72+
{
73+
EdmPrimitiveTypeKind? elementGeographyKind =
74+
EdmSpatialKindMapper.GetGeographyKindForClrType(collectionProperty.RelatedClrType);
75+
76+
if (elementGeographyKind.HasValue)
77+
{
78+
collectionProperty.AsSpatial(elementGeographyKind.Value);
79+
}
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)