/* SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using System; using System.Globalization; using System.Runtime.Serialization; using OpenSearch.Net.Utf8Json; /* * Taken from SolrNet https://github.com/mausch/SolrNet/blob/master/SolrNet/Location.cs */ namespace OpenSearch.Client { /// /// Represents a Latitude/Longitude as a 2 dimensional point that gets serialized as { lat, lon } /// [JsonFormatter(typeof(GeoLocationFormatter))] public class GeoLocation : IEquatable, IFormattable { /// /// Represents a Latitude/Longitude as a 2 dimensional point. /// public GeoLocation(double latitude, double longitude) { Latitude = latitude; Longitude = longitude; } /// /// Latitude /// [DataMember(Name = "lat")] public double Latitude { get; } /// /// Longitude /// [DataMember(Name = "lon")] public double Longitude { get; } [IgnoreDataMember] internal GeoFormat Format { get; set; } public bool Equals(GeoLocation other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Latitude.Equals(other.Latitude) && Longitude.Equals(other.Longitude); } public string ToString(string format, IFormatProvider formatProvider) => ToString(); /// /// Checks if is a valid latitude between -90 and 90, inclusive. /// /// /// public static bool IsValidLatitude(double latitude) => latitude >= -90 && latitude <= 90; /// /// Checks if is a valid longitude between -180 and 180, inclusive. /// /// /// public static bool IsValidLongitude(double longitude) => longitude >= -180 && longitude <= 180; /// /// Try to create a . /// /// /// /// public static GeoLocation TryCreate(double latitude, double longitude) => new GeoLocation(latitude, longitude); public override string ToString() => Latitude.ToString("#0.0#######", CultureInfo.InvariantCulture) + "," + Longitude.ToString("#0.0#######", CultureInfo.InvariantCulture); public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != GetType()) return false; return Equals((GeoLocation)obj); } public override int GetHashCode() => unchecked((Latitude.GetHashCode() * 397) ^ Longitude.GetHashCode()); public static implicit operator GeoLocation(string latLon) { if (string.IsNullOrEmpty(latLon)) throw new ArgumentNullException(nameof(latLon)); var parts = latLon.Split(','); if (parts.Length != 2) throw new ArgumentException("Invalid format: string must be in the form of lat,lon"); if (!double.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var lat)) throw new ArgumentException("Invalid latitude value"); if (!double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var lon)) throw new ArgumentException("Invalid longitude value"); return new GeoLocation(lat, lon); } public static implicit operator GeoLocation(double[] lonLat) => lonLat.Length != 2 ? null : new GeoLocation(lonLat[1], lonLat[0]); } /// /// Represents a Latitude/Longitude and optional Z value as a 2 or 3 dimensional point /// that gets serialized as new [] { lon, lat, [z] } /// [JsonFormatter(typeof(GeoCoordinateFormatter))] public class GeoCoordinate : GeoLocation { /// /// Creates a new instance of /// public GeoCoordinate(double latitude, double longitude) : base(latitude, longitude) { } /// /// Creates a new instance of /// public GeoCoordinate(double latitude, double longitude, double z) : base(latitude, longitude) => Z = z; /// /// Gets or sets the Z value /// public double? Z { get; set; } /// /// Creates a new instance of from an array /// of 2 or 3 doubles, in the order Latitude, Longitude, and optional Z value. /// Returns null if coordinates are null /// If the array does not contain 2 or 3 values /// public static implicit operator GeoCoordinate(double[] coordinates) { if (coordinates == null) return null; switch (coordinates.Length) { case 2: return new GeoCoordinate(coordinates[0], coordinates[1]); case 3: return new GeoCoordinate(coordinates[0], coordinates[1], coordinates[2]); default: throw new ArgumentOutOfRangeException( nameof(coordinates), $"Cannot create a {nameof(GeoCoordinate)} from an array that does not contain 2 or 3 values"); } } } }