Skip to content

Commit

Permalink
Support Microsoft.Spatial unconditionally in Search (#15387)
Browse files Browse the repository at this point in the history
* Fix README for Microsoft.Azure.Core.NewtonsoftJson

* Add Json.NET converters for Microsoft.Spatial

Resolves #13165

* Rename converter to match Json.NET ObjectSerializer

It may be long, but it's consistent.

* Add GeometryFactory and adapter

Uses reflection to loosely bind Microsoft.Spatial types.

* Rename spatial adapters to proxies

* Proxy all supported Microsoft.Spatial types

* Update Search for current Azure.Core.GeoJson

* Encode spatial types for SearchFilter

Fixes #15299

* Implement IEquatable<GeoPosition> on GeoPosition

GeoPosition already implemented the interface, just without declaring it. May as well, so the struct doesn't have to be boxed when using EqualityComparer.Default, for example.

* Resolve PR build issues

* Resolve build analysis issues

Filed #15423 to remove exclusions later.

* Move Microsoft.Spatial support for Json.NET to core

* Remove unused methods

* Always run tests against Microsoft.Spatial

* Change GeometryPoint to GeographyPoint

* Fix README validation failure

* Resolve PR feedback

* Resolve PR feedback
  • Loading branch information
heaths authored Sep 29, 2020
1 parent 605e0d4 commit c865acf
Show file tree
Hide file tree
Showing 44 changed files with 1,631 additions and 198 deletions.
2 changes: 2 additions & 0 deletions eng/.docsettings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ known_content_issues:
- ['sdk/synapse/Microsoft.Azure.Synapse/README.md','#11492']
- ['sdk/tables/Azure.Data.Tables/readme.md', '#11492']
- ['sdk/core/Azure.Core.TestFramework/README.md', '#11492']
- ['sdk/core/Microsoft.Azure.Core.NewtonsoftJson/README.md', '#15423']
- ['sdk/core/Microsoft.Azure.Core.Spatial.NewtonsoftJson/README.md', '#15423']

# .net climbs upwards. placing these to prevent assigning readmes to the wrong project
package_indexing_exclusion_list:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public sealed partial class GeoPolygonCollection : Azure.Core.GeoJson.GeoObject,
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct GeoPosition
public readonly partial struct GeoPosition : System.IEquatable<Azure.Core.GeoJson.GeoPosition>
{
private readonly int _dummyPrimitive;
public GeoPosition(double longitude, double latitude) { throw null; }
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/Azure.Core.Experimental/src/Spatial/GeoPosition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Azure.Core.GeoJson
/// <summary>
///
/// </summary>
public readonly struct GeoPosition
public readonly struct GeoPosition : IEquatable<GeoPosition>
{
/// <summary>
/// Gets the altitude of the position.
Expand Down Expand Up @@ -96,4 +96,4 @@ public override string ToString()
return $"[{Longitude:G17}, {Latitude:G17}, {Altitude.Value:G17}]";
}
}
}
}
12 changes: 12 additions & 0 deletions sdk/core/Azure.Core/Azure.Core.All.sln
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTelemetry.Exporter.Azur
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShareLink", "..\..\keyvault\samples\sharelink\ShareLink.csproj", "{28B02839-0F22-40F2-9A80-C4384881A54F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Core.Spatial.NewtonsoftJson", "..\Microsoft.Azure.Core.Spatial.NewtonsoftJson\src\Microsoft.Azure.Core.Spatial.NewtonsoftJson.csproj", "{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Core.Spatial.NewtonsoftJson.Tests", "..\Microsoft.Azure.Core.Spatial.NewtonsoftJson\tests\Microsoft.Azure.Core.Spatial.NewtonsoftJson.Tests.csproj", "{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
..\..\synapse\Azure.Analytics.Synapse.Shared\src\Azure.Analytics.Synapse.Shared.projitems*{14a1ca9b-2387-4b81-84d5-120e4f8ffab8}*SharedItemsImports = 5
Expand Down Expand Up @@ -1764,6 +1768,14 @@ Global
{28B02839-0F22-40F2-9A80-C4384881A54F}.Release|x64.Build.0 = Release|Any CPU
{28B02839-0F22-40F2-9A80-C4384881A54F}.Release|x86.ActiveCfg = Release|Any CPU
{28B02839-0F22-40F2-9A80-C4384881A54F}.Release|x86.Build.0 = Release|Any CPU
{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}.Release|Any CPU.Build.0 = Release|Any CPU
{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
12 changes: 12 additions & 0 deletions sdk/core/Azure.Core/Azure.Core.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Core.Newton
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Core.NewtonsoftJson.Tests", "..\Microsoft.Azure.Core.NewtonsoftJson\tests\Microsoft.Azure.Core.NewtonsoftJson.Tests.csproj", "{3F70FC18-5F83-4AEE-A9BD-AE100A0B1BF8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Core.Spatial.NewtonsoftJson", "..\Microsoft.Azure.Core.Spatial.NewtonsoftJson\src\Microsoft.Azure.Core.Spatial.NewtonsoftJson.csproj", "{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Core.Spatial.NewtonsoftJson.Tests", "..\Microsoft.Azure.Core.Spatial.NewtonsoftJson\tests\Microsoft.Azure.Core.Spatial.NewtonsoftJson.Tests.csproj", "{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -51,6 +55,14 @@ Global
{3F70FC18-5F83-4AEE-A9BD-AE100A0B1BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F70FC18-5F83-4AEE-A9BD-AE100A0B1BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F70FC18-5F83-4AEE-A9BD-AE100A0B1BF8}.Release|Any CPU.Build.0 = Release|Any CPU
{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2A3FECA-1696-4EDD-ABA1-30F1E3D6CE1D}.Release|Any CPU.Build.0 = Release|Any CPU
{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{622772C8-A2CB-4F8B-82EB-2A46BCC21DAE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
8 changes: 5 additions & 3 deletions sdk/core/Microsoft.Azure.Core.NewtonsoftJson/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Newtonsoft.Json implementation for Azure Core Experimental shared client library for .NET
# Newtonsoft.Json support for Azure Core shared client library for .NET

Azure.Core.Experimental contains types that are being evaluated and might eventually become part of Azure.Core.
This library contains implementations dependent on Newtonsoft.Json, aka JSON.NET, for use with Azure.Core.Experimental.
The [Azure.Core package][azure_core_package] contains types shared by all Azure SDK client libraries.
This library contains converters dependent on the [Newtonsoft.Json package][newtonsoft_json_package] for use with Azure.Core.

## Getting started

Expand Down Expand Up @@ -45,5 +45,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][code_of_con

![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fcore%2FMicrosoft.Azure.Core.NewtonsoftJson%2FREADME.png)

[azure_core_package]: https://www.nuget.org/packages/Azure.Core/
[code_of_conduct]: https://opensource.microsoft.com/codeofconduct
[code_of_conduct_faq]: https://opensource.microsoft.com/codeofconduct/faq/
[newtonsoft_json_package]: https://www.nuget.org/packages/Newtonsoft.Json/
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Implementation of Azure.Core serialization and type discovery for Newtonsoft.Json</Description>
<AssemblyTitle>Microsoft Azure Implementation for Newtonsoft.Json</AssemblyTitle>
<RootNamespace>Azure.Core</RootNamespace>
<AssemblyTitle>Newtonsoft.Json support for Azure Core shared client library for .NET</AssemblyTitle>
<RootNamespace>Azure.Core.Serialization</RootNamespace>
<Version>1.0.0-preview.2</Version>
<PackageTags>Microsoft Azure Newtonsoft Json</PackageTags>
<Description>This library contains converters dependent on the Newtonsoft.Json package for use with Azure.Core.</Description>
<PackageTags>Microsoft Azure SDK Newtonsoft Json</PackageTags>
<Nullable>enable</Nullable>
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
<EnableClientSdkAnalyzers>false</EnableClientSdkAnalyzers>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Release History

## 1.0.0-preview.1 (Unreleased)

### Added

- Added `NewtonsoftJsonGeographyPointConverter` to serialize `Microsoft.Spatial.GeographyPoint` objects.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IsClientLibrary>true</IsClientLibrary>
</PropertyGroup>

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory).., Directory.Build.props))\Directory.Build.props" />
</Project>
52 changes: 52 additions & 0 deletions sdk/core/Microsoft.Azure.Core.Spatial.NewtonsoftJson/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Newtonsoft.Json support for Microsoft.Spatial library for .NET

The [Microsoft.Spatial package][microsoft_spatial_package] contains classes and methods that support geographic and geometric operations.
This library contains converters dependent on the [Newtonsoft.Json package][newtonsoft_json_package] for use with Microsoft.Spatial
when using the Azure SDK for .NET.

## Getting started

TODO

### Install the package

TODO

### Prerequisites

TODO

### Authenticate the client

TODO

## Key concepts

TODO

## Examples

TODO

## Troubleshooting

TODO

## Next steps

TODO

## Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit <https://cla.microsoft.com>.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the [Microsoft Open Source Code of Conduct][code_of_conduct]. For more information see the [Code of Conduct FAQ][code_of_conduct_faq] or contact opencode@microsoft.com with any additional questions or comments.

![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fcore%2FMicrosoft.Azure.Core.NewtonsoftJson%2FREADME.png)

[code_of_conduct]: https://opensource.microsoft.com/codeofconduct
[code_of_conduct_faq]: https://opensource.microsoft.com/codeofconduct/faq/
[microsoft_spatial_package]: https://www.nuget.org/packages/Microsoft.Spatial/
[newtonsoft_json_package]: https://www.nuget.org/packages/Newtonsoft.Json/
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Azure.Core.Serialization
{
public partial class NewtonsoftJsonGeographyPointConverter : Newtonsoft.Json.JsonConverter
{
public NewtonsoftJsonGeographyPointConverter() { }
public override bool CanConvert(System.Type objectType) { throw null; }
public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) { throw null; }
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using Microsoft.Spatial;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Azure.Core.Serialization
{
/// <summary>
/// Defines extension methods for various JSON.NET types that make it easier to recognize and read Geo-JSON.
/// </summary>
internal static class GeoJsonExtensions
{
private const string Coordinates = "coordinates";
private const string Crs = "crs";
private const string Name = "name";
private const string Point = "Point";
private const string Properties = "properties";
private const string Type = "type";
private const string WorldGeodeticSystem1984 = "EPSG:4326"; // See https://epsg.io/4326

private static readonly IEnumerable<string> CrsOnly = new[] { Crs };
private static readonly IEnumerable<string> NameOnly = new[] { Name };
private static readonly IEnumerable<string> TypeAndCoordinates = new[] { Type, Coordinates };
private static readonly IEnumerable<string> TypeAndProperties = new[] { Type, Properties };

/// <summary>
/// Reads a Geo-JSON point into a <see cref="GeographyPoint" /> instance, or throws
/// <see cref="JsonSerializationException" /> if the reader is not positioned on the
/// beginning of a valid Geo-JSON point.
/// </summary>
/// <param name="reader">The JSON reader from which to read a Geo-JSON point.</param>
/// <returns>A <see cref="GeographyPoint" /> instance.</returns>
public static GeographyPoint ReadGeoJsonPoint(this JsonReader reader)
{
// Check for null first.
if (reader.TokenType == JsonToken.Null)
{
return null;
}

GeographyPoint result = null;

reader.ReadObject(
requiredProperties: TypeAndCoordinates,
optionalProperties: CrsOnly,
readProperty: (r, propertyName) =>
{
switch (propertyName)
{
case Type:
r.ExpectAndAdvance(JsonToken.String, Point);
break;
case Coordinates:
result = ReadCoordinates(r);
break;
case Crs:
ReadCrs(r);
break;
}
});

return result;
}

/// <summary>
/// Writes a <see cref="GeographyPoint" /> instance as Geo-JSON format.
/// </summary>
/// <param name="writer">The JSON writer to which to write the Geo-JSON point.</param>
/// <param name="point">The <see cref="GeographyPoint" /> instance to write.</param>
public static void WriteGeoJsonPoint(this JsonWriter writer, GeographyPoint point)
{
writer.WriteStartObject();
writer.WritePropertyName(Type);
writer.WriteValue(Point);
writer.WritePropertyName(Coordinates);
writer.WriteStartArray();
writer.WriteValue(point.Longitude);
writer.WriteValue(point.Latitude);
writer.WriteEndArray();
writer.WriteEndObject();
}

private static bool IsCrsProperties(JObject possibleProperties) =>
possibleProperties.IsValid(
requiredProperties: NameOnly,
isPropertyValid: property => property.Name == Name && property.Value.IsString(WorldGeodeticSystem1984));

private static void ReadCrsProperties(JsonReader propertiesReader) =>
propertiesReader.ReadObjectAndAdvance(
requiredProperties: NameOnly,
readProperty: (r, _) => r.ExpectAndAdvance(JsonToken.String, WorldGeodeticSystem1984));

private static void ReadCrs(JsonReader crsReader) =>
crsReader.ReadObjectAndAdvance(
requiredProperties: TypeAndProperties,
readProperty: (r, propertyName) =>
{
switch (propertyName)
{
case Type:
r.ExpectAndAdvance(JsonToken.String, Name);
break;
case Properties:
ReadCrsProperties(r);
break;
}
});

private static GeographyPoint ReadCoordinates(JsonReader coordinatesReader)
{
coordinatesReader.ExpectAndAdvance(JsonToken.StartArray);

double ReadFloatOrInt()
{
switch (coordinatesReader.TokenType)
{
case JsonToken.Integer:
return coordinatesReader.ExpectAndAdvance<long>(JsonToken.Integer);

// Treat all other cases as Float and let ExpectAndAdvance() handle any errors.
default:
return coordinatesReader.ExpectAndAdvance<double>(JsonToken.Float);
}
}

double longitude = ReadFloatOrInt();
double latitude = ReadFloatOrInt();

coordinatesReader.ExpectAndAdvance(JsonToken.EndArray);
return GeographyPoint.Create(latitude, longitude);
}
}
}
Loading

0 comments on commit c865acf

Please sign in to comment.