< Summary

Class:Microsoft.Azure.Search.Serialization.GeoJsonExtensions
Assembly:Microsoft.Azure.Search.Data
File(s):C:\Git\azure-sdk-for-net\sdk\search\Microsoft.Azure.Search.Data\src\Customizations\Serialization\GeoJsonExtensions.cs
Covered lines:103
Uncovered lines:0
Coverable lines:103
Total lines:187
Line coverage:100% (103 of 103)
Covered branches:31
Total branches:38
Branch coverage:81.5% (31 of 38)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-100%100%
IsGeoJsonPoint(...)-100%90%
ReadGeoJsonPoint(...)-100%87.5%
WriteGeoJsonPoint(...)-100%100%
IsCrsProperties(...)-100%100%
ReadCrsProperties(...)-100%100%
IsCrs(...)-100%83.33%
ReadCrs(...)-100%75%
AreCoordinates(...)-100%50%
ReadCoordinates(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\search\Microsoft.Azure.Search.Data\src\Customizations\Serialization\GeoJsonExtensions.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License. See License.txt in the project root for
 3// license information.
 4
 5using System.Collections.Generic;
 6using Microsoft.Spatial;
 7using Newtonsoft.Json;
 8using Newtonsoft.Json.Linq;
 9
 10namespace Microsoft.Azure.Search.Serialization
 11{
 12    /// <summary>
 13    /// Defines extension methods for various JSON.NET types that make it easier to recognize and read Geo-JSON.
 14    /// </summary>
 15    public static class GeoJsonExtensions
 16    {
 17        private const string Coordinates = "coordinates";
 18        private const string Crs = "crs";
 19        private const string Name = "name";
 20        private const string Point = "Point";
 21        private const string Properties = "properties";
 22        private const string Type = "type";
 23        private const string WorldGeodeticSystem1984 = "EPSG:4326"; // See https://epsg.io/4326
 24
 225        private static readonly IEnumerable<string> CrsOnly = new[] { Crs };
 226        private static readonly IEnumerable<string> NameOnly = new[] { Name };
 227        private static readonly IEnumerable<string> TypeAndCoordinates = new[] { Type, Coordinates };
 228        private static readonly IEnumerable<string> TypeAndProperties = new[] { Type, Properties };
 29
 30        /// <summary>
 31        /// Determines whether the given <c cref="JObject">JObject</c> is a valid Geo-JSON point.
 32        /// </summary>
 33        /// <param name="obj">The JSON object to test.</param>
 34        /// <returns><c>true</c> if the JSON object is not null and is a valid Geo-JSON point, <c>false</c> otherwise.</
 35        public static bool IsGeoJsonPoint(this JObject obj) =>
 31636            obj?.IsValid(
 31637                requiredProperties: TypeAndCoordinates,
 31638                isPropertyValid: property =>
 31639                {
 87640                    switch (property.Name)
 31641                    {
 31642                        case Type:
 45443                            return property.Value.IsString(Point);
 31644
 31645                        case Coordinates:
 45046                            return AreCoordinates(property.Value);
 31647
 31648                        case Crs:
 42849                            return property.Value is JObject possibleCrs && IsCrs(possibleCrs);
 31650
 31651                        default:
 49252                            return false;
 31653                    }
 31654                }) ?? false;
 55
 56        /// <summary>
 57        /// Reads a Geo-JSON point into a <c cref="GeographyPoint">GeographyPoint</c> instance, or throws
 58        /// <c cref="JsonSerializationException">JsonSerializationException</c> if the reader is not positioned on the
 59        /// beginning of a valid Geo-JSON point.
 60        /// </summary>
 61        /// <param name="reader">The JSON reader from which to read a Geo-JSON point.</param>
 62        /// <returns>A <c cref="GeographyPoint">GeographyPoint</c> instance.</returns>
 63        public static GeographyPoint ReadGeoJsonPoint(this JsonReader reader)
 64        {
 65            // Check for null first.
 57666            if (reader.TokenType == JsonToken.Null)
 67            {
 9068                return null;
 69            }
 70
 48671            GeographyPoint result = null;
 72
 48673            reader.ReadObject(
 48674                requiredProperties: TypeAndCoordinates,
 48675                optionalProperties: CrsOnly,
 48676                readProperty: (r, propertyName) =>
 48677                {
 48678                    switch (propertyName)
 48679                    {
 48680                        case Type:
 97081                            r.ExpectAndAdvance(JsonToken.String, Point);
 96682                            break;
 48683
 48684                        case Coordinates:
 96885                            result = ReadCoordinates(r);
 96886                            break;
 48687
 48688                        case Crs:
 94689                            ReadCrs(r);
 48690                            break;
 48691                    }
 94292                });
 93
 47494            return result;
 95        }
 96
 97        /// <summary>
 98        /// Writes a <c cref="GeographyPoint">GeographyPoint</c> instance as Geo-JSON format.
 99        /// </summary>
 100        /// <param name="writer">The JSON writer to which to write the Geo-JSON point.</param>
 101        /// <param name="point">The <c cref="GeographyPoint">GeographyPoint</c> instance to write.</param>
 102        public static void WriteGeoJsonPoint(this JsonWriter writer, GeographyPoint point)
 103        {
 1994104            writer.WriteStartObject();
 1994105            writer.WritePropertyName(Type);
 1994106            writer.WriteValue(Point);
 1994107            writer.WritePropertyName(Coordinates);
 1994108            writer.WriteStartArray();
 1994109            writer.WriteValue(point.Longitude);
 1994110            writer.WriteValue(point.Latitude);
 1994111            writer.WriteEndArray();
 1994112            writer.WriteEndObject();
 1994113        }
 114
 115        private static bool IsCrsProperties(JObject possibleProperties) =>
 112116            possibleProperties.IsValid(
 112117                requiredProperties: NameOnly,
 226118                isPropertyValid: property => property.Name == Name && property.Value.IsString(WorldGeodeticSystem1984));
 119
 120        private static void ReadCrsProperties(JsonReader propertiesReader) =>
 460121            propertiesReader.ReadObjectAndAdvance(
 460122                requiredProperties: NameOnly,
 920123                readProperty: (r, _) => r.ExpectAndAdvance(JsonToken.String, WorldGeodeticSystem1984));
 124
 125        private static bool IsCrs(JObject possibleCrs) =>
 112126            possibleCrs.IsValid(
 112127                requiredProperties: TypeAndProperties,
 112128                isPropertyValid: property =>
 112129                {
 338130                    switch (property.Name)
 112131                    {
 112132                        case Type:
 224133                            return property.Value.IsString(Name);
 112134
 112135                        case Properties:
 224136                            return property.Value is JObject possibleProperties && IsCrsProperties(possibleProperties);
 112137
 112138                        default:
 114139                            return false;
 112140                    }
 112141                });
 142
 143        private static void ReadCrs(JsonReader crsReader) =>
 460144            crsReader.ReadObjectAndAdvance(
 460145                requiredProperties: TypeAndProperties,
 460146                readProperty: (r, propertyName) =>
 460147                {
 460148                    switch (propertyName)
 460149                    {
 460150                        case Type:
 920151                            r.ExpectAndAdvance(JsonToken.String, Name);
 920152                            break;
 460153
 460154                        case Properties:
 920155                            ReadCrsProperties(r);
 460156                            break;
 460157                    }
 918158                });
 159
 160        private static bool AreCoordinates(JToken possibleCoordinates) =>
 134161            possibleCoordinates is JArray array && array.Count == 2 && array[0].IsNumber() && array[1].IsNumber();
 162
 163        private static GeographyPoint ReadCoordinates(JsonReader coordinatesReader)
 164        {
 482165            coordinatesReader.ExpectAndAdvance(JsonToken.StartArray);
 166
 167            double ReadFloatOrInt()
 168            {
 964169                switch (coordinatesReader.TokenType)
 170                {
 171                    case JsonToken.Integer:
 12172                        return coordinatesReader.ExpectAndAdvance<long>(JsonToken.Integer);
 173
 174                    // Treat all other cases as Float and let ExpectAndAdvance() handle any errors.
 175                    default:
 952176                        return coordinatesReader.ExpectAndAdvance<double>(JsonToken.Float);
 177                }
 178            }
 179
 482180            double longitude = ReadFloatOrInt();
 482181            double latitude = ReadFloatOrInt();
 182
 482183            coordinatesReader.ExpectAndAdvance(JsonToken.EndArray);
 482184            return GeographyPoint.Create(latitude, longitude);
 185        }
 186    }
 187}