Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions AspNetCoreOData.NetTopologySuite.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OData", "src\Microsoft.AspNetCore.OData\Microsoft.AspNetCore.OData.csproj", "{79D9A62B-36EB-4345-AFCE-081397C29F7E}"
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0C88DD14-F956-CE84-757C-A364CCF449FC}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|x64.ActiveCfg = Debug|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|x64.Build.0 = Debug|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|x86.ActiveCfg = Debug|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Debug|x86.Build.0 = Debug|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|Any CPU.Build.0 = Release|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|x64.ActiveCfg = Release|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|x64.Build.0 = Release|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|x86.ActiveCfg = Release|Any CPU
{79D9A62B-36EB-4345-AFCE-081397C29F7E}.Release|x86.Build.0 = Release|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|x64.ActiveCfg = Debug|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|x64.Build.0 = Debug|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|x86.ActiveCfg = Debug|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Debug|x86.Build.0 = Debug|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|Any CPU.Build.0 = Release|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|x64.ActiveCfg = Release|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|x64.Build.0 = Release|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|x86.ActiveCfg = Release|Any CPU
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620}.Release|x86.Build.0 = Release|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|x64.ActiveCfg = Debug|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|x64.Build.0 = Debug|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|x86.ActiveCfg = Debug|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Debug|x86.Build.0 = Debug|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|Any CPU.Build.0 = Release|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|x64.ActiveCfg = Release|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|x64.Build.0 = Release|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|x86.ActiveCfg = Release|Any CPU
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{79D9A62B-36EB-4345-AFCE-081397C29F7E} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{57ECAA5E-54AB-46AD-A7C6-981BF39E3620} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{5E8F75EF-B325-4564-8EAF-A39BAC8FE4D8} = {0C88DD14-F956-CE84-757C-A364CCF449FC}
EndGlobalSection
EndGlobal
38 changes: 38 additions & 0 deletions src/Microsoft.AspNetCore.OData.NetTopologySuite.Nightly.nuspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>Microsoft.AspNetCore.OData.NetTopologySuite</id>
<title>ASP.NET Core OData NetTopologySuite Integration</title>
<version>$VersionFullSemantic$-Nightly$NightlyBuildVersion$</version>
<authors>Microsoft</authors>
<copyright>&#169; Microsoft Corporation. All rights reserved.</copyright>
<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>
<summary>NetTopologySuite spatial support for ASP.NET Core OData.</summary>
<language>en-US</language>
<projectUrl>http://github.com/OData/AspNetCoreOData</projectUrl>
<license type="expression">MIT</license>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<tags>Microsoft AspNetCore WebApi OData</tags>
<readme>docs\README.md</readme>
<icon>images\odata.png</icon>
<repository type="git" url="https://github.com/OData/AspNetCoreOData.git" branch="master" />
<dependencies>
<group targetFramework=".net10.0">
<dependency id="Microsoft.AspNetCore.OData" version="$ODataAspNetCorePackageDependency$" />
<dependency id="Microsoft.OData.ModelBuilder" version="$ODataModelBuilderPackageDependency$" />
<dependency id="Microsoft.OData.Core" version="$ODataLibPackageDependency$" />
<dependency id="Microsoft.OData.Edm" version="$ODataLibPackageDependency$" />
<dependency id="Microsoft.Spatial" version="$ODataLibPackageDependency$" />
</group>
</dependencies>
</metadata>
<files>
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.dll" target="lib\net10.0" />
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.xml" target="lib\net10.0" />
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.pdb" target="lib\net10.0" />
<file src="$SourcesRoot$\images\odata.png" target="images\" />

<!-- readme file -->
<file src="$SourcesRoot$\README.md" target="docs\" />
</files>
</package>
38 changes: 38 additions & 0 deletions src/Microsoft.AspNetCore.OData.NetTopologySuite.Release.nuspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>Microsoft.AspNetCore.OData.NetTopologySuite</id>
<title>ASP.NET Core OData NetTopologySuite Integration</title>
<version>$VersionNuGetSemantic$</version>
<authors>Microsoft</authors>
<copyright>&#169; Microsoft Corporation. All rights reserved.</copyright>
<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>
<summary>NetTopologySuite spatial support for ASP.NET Core OData.</summary>
<language>en-US</language>
<projectUrl>http://github.com/OData/AspNetCoreOData</projectUrl>
<license type="expression">MIT</license>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<tags>Microsoft AspNetCore OData NetTopologySuite</tags>
<readme>docs\README.md</readme>
<icon>images\odata.png</icon>
<repository type="git" url="https://github.com/OData/AspNetCoreOData.git" branch="master" />
<dependencies>
<group targetFramework=".net10.0">
<dependency id="Microsoft.AspNetCore.OData" version="$ODataAspNetCorePackageDependency$" />
<dependency id="Microsoft.OData.ModelBuilder" version="$ODataModelBuilderPackageDependency$" />
<dependency id="Microsoft.OData.Core" version="$ODataLibPackageDependency$" />
<dependency id="Microsoft.OData.Edm" version="$ODataLibPackageDependency$" />
<dependency id="Microsoft.Spatial" version="$ODataLibPackageDependency$" />
</group>
</dependencies>
</metadata>
<files>
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.dll" target="lib\net10.0" />
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.xml" target="lib\net10.0" />
<file src="$ProductRoot$\net10.0\Microsoft.AspNetCore.OData.NetTopologySuite.pdb" target="lib\net10.0" />
<file src="$SourcesRoot$\images\odata.png" target="images\" />

<!-- readme file -->
<file src="$SourcesRoot$\README.md" target="docs\" />
</files>
</package>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//-----------------------------------------------------------------------------
// <copyright file="GeographyAttribute.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using Microsoft.AspNetCore.OData.NetTopologySuite.Conventions;
using Microsoft.OData.Edm;
using Geometry = NetTopologySuite.Geometries.Geometry;

namespace Microsoft.AspNetCore.OData.NetTopologySuite.Attributes;

/// <summary>
/// Marks a property or CLR type so that NetTopologySuite geometry values are modeled as Edm geography types.
/// </summary>
/// <remarks>
/// - When applied to a property whose CLR type derives from <see cref="Geometry"/>,
/// the property is mapped to the corresponding <see cref="EdmPrimitiveTypeKind"/> in the Edm.Geography* family
/// (for example, Point → GeographyPoint).
/// - When applied to a class, all properties on the type whose CLR types derive from
/// <see cref="Geometry"/> are treated as Edm.Geography*.
/// The behavior is enforced by the conventions
/// <see cref="GeographyAttributeEdmPropertyConvention"/> and
/// <see cref="GeographyAttributeEdmTypeConvention"/>.
/// </remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class GeographyAttribute : Attribute
{
}
58 changes: 58 additions & 0 deletions src/Microsoft.AspNetCore.OData.NetTopologySuite/Common/Error.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// <copyright file="Error.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System.Diagnostics.CodeAnalysis;
using System.Globalization;

namespace Microsoft.AspNetCore.OData.NetTopologySuite.Common;

[ExcludeFromCodeCoverage]
internal static class Error
{
/// <summary>
/// Formats the specified resource string using <see cref="CultureInfo.CurrentCulture"/>.
/// </summary>
/// <param name="format">A composite format string.</param>
/// <param name="args">An object array that contains zero or more objects to format.</param>
/// <returns>The formatted string.</returns>
internal static string Format(string format, params object[] args)
{
return String.Format(CultureInfo.CurrentCulture, format, args);
}

/// <summary>
/// Creates an <see cref="ArgumentNullException"/> with the provided properties.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentNullException ArgumentNull(string parameterName)
{
return new ArgumentNullException(parameterName);
}

/// <summary>
/// Creates an <see cref="InvalidOperationException"/>.
/// </summary>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static InvalidOperationException InvalidOperation(string messageFormat, params object[] messageArgs)
{
return new InvalidOperationException(Error.Format(messageFormat, messageArgs));
}

/// <summary>
/// Creates an <see cref="NotSupportedException"/>.
/// </summary>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static NotSupportedException NotSupported(string messageFormat, params object[] messageArgs)
{
return new NotSupportedException(Error.Format(messageFormat, messageArgs));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//-----------------------------------------------------------------------------
// <copyright file="GeographyAttributeEdmPropertyConvention.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using Microsoft.AspNetCore.OData.NetTopologySuite.Attributes;
using Microsoft.AspNetCore.OData.NetTopologySuite.Common;
using Microsoft.AspNetCore.OData.NetTopologySuite.Edm;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.ModelBuilder.Conventions.Attributes;
using Geometry = NetTopologySuite.Geometries.Geometry;

namespace Microsoft.AspNetCore.OData.NetTopologySuite.Conventions;

/// <summary>
/// A convention that applies to individual properties decorated with the <see cref="GeographyAttribute"/>.
/// If the property’s CLR type is a NetTopologySuite spatial type, it is mapped to the corresponding
/// OData geography Edm type (for example, <c>Point</c> → <c>Edm.GeographyPoint</c>,
/// <c>LineString</c> → <c>Edm.GeographyLineString</c>).
/// </summary>
public class GeographyAttributeEdmPropertyConvention : AttributeEdmPropertyConvention<PropertyConfiguration>
{
/// <summary>
/// Initializes a new instance of the <see cref="GeographyAttributeEdmPropertyConvention"/> class.
/// Configures the convention to match properties annotated with <see cref="GeographyAttribute"/> and
/// to map NetTopologySuite spatial CLR types to their Edm.Geography* primitive kinds.
/// </summary>
public GeographyAttributeEdmPropertyConvention()
: base(attribute => attribute.GetType() == typeof(GeographyAttribute), allowMultiple: false)
{

}

/// <inheritdoc/>
public override void Apply(PropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute, ODataConventionModelBuilder model)
{
if (edmProperty == null)
{
throw Error.ArgumentNull(nameof(edmProperty));
}

if (structuralTypeConfiguration == null)
{
throw Error.ArgumentNull(nameof(structuralTypeConfiguration));
}

if (model == null)
{
throw Error.ArgumentNull(nameof(model));
}

// If the property was added explicitly, we do not apply the convention.
if (edmProperty.AddedExplicitly)
{
return;
}

if (edmProperty is PrimitivePropertyConfiguration primitiveProperty
&& typeof(Geometry).IsAssignableFrom(edmProperty.RelatedClrType))
{
EdmPrimitiveTypeKind? targetPrimitiveTypeKind = EdmSpatialKindMapper.GetGeographyKindForClrType(edmProperty.RelatedClrType);
if (targetPrimitiveTypeKind.HasValue)
{
primitiveProperty.AsSpatial(targetPrimitiveTypeKind.Value);
}
}
else if (edmProperty is CollectionPropertyConfiguration collectionProperty
&& typeof(Geometry).IsAssignableFrom(collectionProperty.RelatedClrType))
{
EdmPrimitiveTypeKind? elementGeographyKind =
EdmSpatialKindMapper.GetGeographyKindForClrType(collectionProperty.RelatedClrType);

if (elementGeographyKind.HasValue)
{
collectionProperty.AsSpatial(elementGeographyKind.Value);
}
}
}
}
Loading