diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs index 394d0939..6a2ace70 100644 --- a/src/GlobalSuppressions.cs +++ b/src/GlobalSuppressions.cs @@ -49,6 +49,7 @@ [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Restier.Providers.EntityFramework")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Restier.Providers.EntityFramework.Model")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Restier.Providers.EntityFramework.Query")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Restier.Providers.EntityFramework.Spatial")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Restier.Providers.EntityFramework.Submit")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Restier.Publishers.OData")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Restier.Publishers.OData.Batch")] diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Microsoft.Restier.Providers.EntityFramework.csproj b/src/Microsoft.Restier.Providers.EntityFramework/Microsoft.Restier.Providers.EntityFramework.csproj index 15355fe2..906517f4 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Microsoft.Restier.Providers.EntityFramework.csproj +++ b/src/Microsoft.Restier.Providers.EntityFramework/Microsoft.Restier.Providers.EntityFramework.csproj @@ -29,6 +29,10 @@ ..\..\packages\Microsoft.OData.Edm.6.15.0\lib\portable-net45+win+wpa81\Microsoft.OData.Edm.dll True + + ..\..\packages\Microsoft.Spatial.6.15.0\lib\portable-net45+win+wpa81\Microsoft.Spatial.dll + True + @@ -77,6 +81,7 @@ Resources.resx + diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.Designer.cs b/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.Designer.cs index 6d1dc91f..27013733 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.Designer.cs +++ b/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.Designer.cs @@ -69,6 +69,24 @@ internal static string DataModificationMustBeCUD { } } + /// + /// Looks up a localized string similar to Need 'LineString type', while input is {0}.. + /// + internal static string InvalidLineStringGeographyType { + get { + return ResourceManager.GetString("InvalidLineStringGeographyType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Need 'Point type', while input is {0}.. + /// + internal static string InvalidPointGeographyType { + get { + return ResourceManager.GetString("InvalidPointGeographyType", resourceCulture); + } + } + /// /// Looks up a localized string similar to The precondition check for request {0} on resource {1} is failed.. /// diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.resx b/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.resx index e49873a3..dd1d9dd8 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.resx +++ b/src/Microsoft.Restier.Providers.EntityFramework/Properties/Resources.resx @@ -120,6 +120,12 @@ A DataModificationEntry must be either New, Update or Delete. + + Need 'LineString type', while input is {0}. + + + Need 'Point type', while input is {0}. + The precondition check for request {0} on resource {1} is failed. diff --git a/src/Microsoft.Restier.Providers.EntityFramework/Spatial/GeographyConverter.cs b/src/Microsoft.Restier.Providers.EntityFramework/Spatial/GeographyConverter.cs new file mode 100644 index 00000000..7033b123 --- /dev/null +++ b/src/Microsoft.Restier.Providers.EntityFramework/Spatial/GeographyConverter.cs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Data.Entity.Spatial; +using System.Globalization; +using System.Text; +using Microsoft.Restier.Providers.EntityFramework.Properties; +using Microsoft.Spatial; + +namespace Microsoft.Restier.Providers.EntityFramework.Spatial +{ + /// + /// The class defined conversion between GeographyPoint and DbGeography, + /// and between GeographyLineString and DbGeography. + /// + public static class GeographyConverter + { + private static readonly CultureInfo DefaultCulture = CultureInfo.GetCultureInfo("En-Us"); + private const string GeographyTypeNamePoint = "Point"; + private const string GeographyTypeNameLineString = "LineString"; + + public static GeographyPoint ToGeographyPoint(this DbGeography geography) + { + if (geography == null) + { + return null; + } + + if (geography.SpatialTypeName != GeographyTypeNamePoint) + { + throw new InvalidOperationException(string.Format( + CultureInfo.InvariantCulture, + Resources.InvalidPointGeographyType, + geography.SpatialTypeName)); + } + + double lat = geography.Latitude ?? 0; + double lon = geography.Longitude ?? 0; + double? alt = geography.Elevation; + double? m = geography.Measure; + return GeographyPoint.Create(lat, lon, alt, m); + } + + public static DbGeography ToDbGeography(this GeographyPoint point) + { + if (point == null) + { + return null; + } + + string text = "POINT(" + point.Latitude.ToString(DefaultCulture) + " " + + point.Longitude.ToString(DefaultCulture); + + if (point.Z.HasValue) + { + text += " " + point.Z.Value; + } + + if (point.M.HasValue) + { + text += " " + point.M.Value; + } + + text += ")"; + + return DbGeography.FromText(text); + } + + public static GeographyLineString ToGeographyLineString(this DbGeography geography) + { + if (geography == null) + { + return null; + } + + if (geography.SpatialTypeName != GeographyTypeNameLineString) + { + throw new InvalidOperationException(string.Format( + CultureInfo.InvariantCulture, + Resources.InvalidLineStringGeographyType, + geography.SpatialTypeName)); + } + + SpatialBuilder builder = SpatialBuilder.Create(); + GeographyPipeline pipleLine = builder.GeographyPipeline; + pipleLine.SetCoordinateSystem(CoordinateSystem.DefaultGeography); + pipleLine.BeginGeography(SpatialType.LineString); + + int numPoints = geography.PointCount ?? 0; + if (numPoints > 0) + { + DbGeography point = geography.PointAt(1); + pipleLine.BeginFigure(new GeographyPosition(point.Latitude ?? 0, point.Latitude ?? 0, point.Elevation, point.Measure)); + + for (int n = 2; n <= numPoints; n++) + { + point = geography.PointAt(n); + pipleLine.LineTo(new GeographyPosition(point.Latitude ?? 0, point.Latitude ?? 0, point.Elevation, point.Measure)); + } + + pipleLine.EndFigure(); + } + + pipleLine.EndGeography(); + GeographyLineString lineString = (GeographyLineString)builder.ConstructedGeography; + return lineString; + } + + public static DbGeography ToDbGeography(this GeographyLineString lineString) + { + if (lineString == null) + { + return null; + } + + StringBuilder sb = new StringBuilder("LINESTRING("); + int n = 0; + foreach (var pt in lineString.Points) + { + double lat = pt.Latitude; + double lon = pt.Longitude; + double? alt = pt.Z; + double? m = pt.M; + + string pointStr = lat.ToString(DefaultCulture) + " " + lon.ToString(DefaultCulture); + + if (alt != null) + { + pointStr += " " + alt.Value; + } + + if (m != null) + { + pointStr += " " + m.Value; + } + + sb.Append(pointStr); + n++; + if (n != lineString.Points.Count) + { + sb.Append(","); + } + } + sb.Append(")"); + + return DbGeography.FromText(sb.ToString()); + } + } +} diff --git a/src/Microsoft.Restier.Providers.EntityFramework/packages.config b/src/Microsoft.Restier.Providers.EntityFramework/packages.config index 4578ec47..24c7de60 100644 --- a/src/Microsoft.Restier.Providers.EntityFramework/packages.config +++ b/src/Microsoft.Restier.Providers.EntityFramework/packages.config @@ -3,6 +3,7 @@ + diff --git a/test/Microsoft.Restier.TestCommon/PublicApi.bsl b/test/Microsoft.Restier.TestCommon/PublicApi.bsl index 91553af4..906f6b91 100644 --- a/test/Microsoft.Restier.TestCommon/PublicApi.bsl +++ b/test/Microsoft.Restier.TestCommon/PublicApi.bsl @@ -608,6 +608,31 @@ public class Microsoft.Restier.Publishers.OData.RestierPayloadValueConverter : M public virtual object ConvertToPayloadValue (object value, Microsoft.OData.Edm.IEdmTypeReference edmTypeReference) } +[ +ExtensionAttribute(), +] +public sealed class Microsoft.Restier.Providers.EntityFramework.Spatial.GeographyConverter { + [ + ExtensionAttribute(), + ] + public static DbGeography ToDbGeography (Microsoft.Spatial.GeographyLineString lineString) + + [ + ExtensionAttribute(), + ] + public static DbGeography ToDbGeography (Microsoft.Spatial.GeographyPoint point) + + [ + ExtensionAttribute(), + ] + public static Microsoft.Spatial.GeographyLineString ToGeographyLineString (DbGeography geography) + + [ + ExtensionAttribute(), + ] + public static Microsoft.Spatial.GeographyPoint ToGeographyPoint (DbGeography geography) +} + public class Microsoft.Restier.Publishers.OData.Batch.RestierBatchChangeSetRequestItem : System.Web.OData.Batch.ChangeSetRequestItem, IDisposable { public RestierBatchChangeSetRequestItem (System.Collections.Generic.IEnumerable`1[[System.Net.Http.HttpRequestMessage]] requests, System.Func`1[[Microsoft.Restier.Core.ApiBase]] apiFactory)