@@ -10,25 +10,25 @@ namespace PolylineAlgorithm.Abstraction;
1010using System . Runtime . CompilerServices ;
1111
1212/// <summary>
13- /// Provides methods for encoding and decoding geographic coordinates to and from polyline strings.
14- /// Supports normalization, denormalization, and efficient value encoding/decoding for polyline algorithms .
13+ /// Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and denormalizing
14+ /// geographic coordinate values .
1515/// </summary>
16+ /// <remarks>The <see cref="PolylineEncoding"/> class includes functionality for working with encoded polyline
17+ /// data, such as reading and writing encoded values, as well as methods for normalizing and denormalizing geographic
18+ /// coordinates. It also provides validation utilities to ensure values conform to expected ranges for latitude and
19+ /// longitude.</remarks>
1620public static class PolylineEncoding {
1721 /// <summary>
18- /// Attempts to read an encoded value from the specified buffer and update the provided variance.
22+ /// Attempts to read a value from the specified buffer and updates the variance.
1923 /// </summary>
20- /// <param name="variance">
21- /// The current variance to update with the decoded value.
22- /// </param>
23- /// <param name="buffer">
24- /// The buffer containing the encoded polyline data.
25- /// </param>
26- /// <param name="position">
27- /// The current position in the buffer. This value is updated as the value is read.
28- /// </param>
29- /// <returns>
30- /// <see langword="true"/> if a value was successfully read; otherwise, <see langword="false"/>.
31- /// </returns>
24+ /// <remarks>This method processes the buffer starting at the specified position and attempts to decode a value.
25+ /// The decoded value is used to update the <paramref name="variance"/> parameter. The method stops reading when a
26+ /// termination condition is met or the end of the buffer is reached.</remarks>
27+ /// <param name="variance">A reference to the integer that will be updated based on the value read from the buffer.</param>
28+ /// <param name="buffer">A reference to the read-only memory buffer containing the data to be processed.</param>
29+ /// <param name="position">A reference to the current position within the buffer. The position is incremented as the method reads data.</param>
30+ /// <returns><see langword="true"/> if a value was successfully read and the end of the buffer was not reached; otherwise, <see
31+ /// langword="false"/>.</returns>
3232 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
3333 public static bool TryReadValue ( ref int variance , ref ReadOnlyMemory < char > buffer , ref int position ) {
3434 if ( position == buffer . Length ) {
@@ -56,32 +56,78 @@ public static bool TryReadValue(ref int variance, ref ReadOnlyMemory<char> buffe
5656 }
5757
5858 /// <summary>
59- /// Converts a normalized integer value back to its original double representation.
59+ /// Converts a normalized integer value to its denormalized double representation based on the specified type .
6060 /// </summary>
61- /// <param name="value">
62- /// The normalized integer value to denormalize.
63- /// </param>
64- /// <returns>
65- /// The denormalized double value.
66- /// </returns>
61+ /// <remarks>The denormalization process divides the input value by a predefined precision factor to
62+ /// produce the resulting double. Ensure that <paramref name="value"/> is validated against the specified <paramref
63+ /// name="type"/> before calling this method.</remarks>
64+ /// <param name="value">The normalized integer value to be denormalized. Must be within the valid range for the specified <paramref
65+ /// name="type"/>.</param>
66+ /// <param name="type">The type that defines the valid range for <paramref name="value"/>.</param>
67+ /// <returns>The denormalized double representation of the input value. Returns <see langword="0.0"/> if <paramref
68+ /// name="value"/> is <see langword="0"/>.</returns>
69+ /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is outside the valid range for the specified <paramref name="type"/>.</exception>
6770 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
68- public static double Denormalize ( int value ) => Math . Truncate ( ( double ) value ) / Defaults . Algorithm . Precision ;
71+ public static double Denormalize ( int value , ValueType type ) {
72+ if ( ! ValidateNormalizedValue ( value , type ) ) {
73+ throw new ArgumentOutOfRangeException ( nameof ( value ) , value , "Value is out of range for the specified type." ) ;
74+ }
75+
76+ if ( value == 0 ) {
77+ return 0.0 ;
78+ }
79+
80+ return Math . Truncate ( ( double ) value ) / Defaults . Algorithm . Precision ;
81+ }
82+
83+ /// <summary>
84+ /// Validates whether the specified normalized value falls within the acceptable range for the given value type.
85+ /// </summary>
86+ /// <remarks>The valid range for normalized values depends on the specified <paramref name="type"/>: <list
87+ /// type="bullet"> <item> <description> For <see cref="ValueType.Latitude"/>, the value must be between
88+ /// <c>Defaults.Coordinate.Latitude.Normalized.Min</c> and <c>Defaults.Coordinate.Latitude.Normalized.Max</c>,
89+ /// inclusive. </description> </item> <item> <description> For <see cref="ValueType.Longitude"/>, the value must be
90+ /// between <c>Defaults.Coordinate.Longitude.Normalized.Min</c> and
91+ /// <c>Defaults.Coordinate.Longitude.Normalized.Max</c>, inclusive. </description> </item> </list> Any other
92+ /// <paramref name="type"/> will result in the method returning <see langword="false"/>.</remarks>
93+ /// <param name="value">The normalized value to validate. Must be an integer.</param>
94+ /// <param name="type">The type of value to validate, such as <see cref="ValueType.Latitude"/> or <see cref="ValueType.Longitude"/>.</param>
95+ /// <returns><see langword="true"/> if the normalized value is within the valid range for the specified value type;
96+ /// otherwise, <see langword="false"/>.</returns>
97+ public static bool ValidateNormalizedValue ( int value , ValueType type ) => ( type , value ) switch {
98+ ( ValueType . Latitude , int normalized ) when normalized >= Defaults . Coordinate . Latitude . Normalized . Min && normalized <= Defaults . Coordinate . Latitude . Normalized . Max => true ,
99+ ( ValueType . Longitude , int normalized ) when normalized >= Defaults . Coordinate . Longitude . Normalized . Min && normalized <= Defaults . Coordinate . Longitude . Normalized . Max => true ,
100+ _ => false ,
101+ } ;
102+
103+ /// <summary>
104+ /// Validates whether the specified denormalized value falls within the acceptable range for the given value type.
105+ /// </summary>
106+ /// <remarks>The valid ranges for latitude and longitude are defined by <see
107+ /// cref="Defaults.Coordinate.Latitude.Min"/>, <see cref="Defaults.Coordinate.Latitude.Max"/>, <see
108+ /// cref="Defaults.Coordinate.Longitude.Min"/>, and <see cref="Defaults.Coordinate.Longitude.Max"/>.</remarks>
109+ /// <param name="value">The denormalized value to validate.</param>
110+ /// <param name="type">The type of value to validate, such as latitude or longitude.</param>
111+ /// <returns><see langword="true"/> if the <paramref name="value"/> is within the valid range for the specified <paramref
112+ /// name="type"/>; otherwise, <see langword="false"/>.</returns>
113+ public static bool ValidateDenormalizedValue ( double value , ValueType type ) => ( type , value ) switch {
114+ ( ValueType . Latitude , double denormalized ) when denormalized >= Defaults . Coordinate . Latitude . Min && denormalized <= Defaults . Coordinate . Latitude . Max => true ,
115+ ( ValueType . Longitude , double denormalized ) when denormalized >= Defaults . Coordinate . Longitude . Min && denormalized <= Defaults . Coordinate . Longitude . Max => true ,
116+ _ => false ,
117+ } ;
69118
70119 /// <summary>
71- /// Attempts to write an encoded value to the specified buffer.
120+ /// Attempts to write a value derived from the specified <paramref name="variance"/> into the provided <paramref
121+ /// name="buffer"/> at the given <paramref name="position"/>.
72122 /// </summary>
73- /// <param name="variance">
74- /// The variance value to encode.
75- /// </param>
76- /// <param name="buffer">
77- /// The buffer to write the encoded value to.
78- /// </param>
79- /// <param name="position">
80- /// The current position in the buffer. This value is updated as the value is written.
81- /// </param>
82- /// <returns>
83- /// <see langword="true"/> if the value was successfully written; otherwise, <see langword="false"/>.
84- /// </returns>
123+ /// <remarks>This method performs bounds checking to ensure that the buffer has sufficient space to
124+ /// accommodate the calculated value. If the buffer does not have enough space, the method returns <see
125+ /// langword="false"/> without modifying the buffer or position.</remarks>
126+ /// <param name="variance">The integer value used to calculate the output to be written into the buffer.</param>
127+ /// <param name="buffer">A reference to the span of characters where the value will be written.</param>
128+ /// <param name="position">A reference to the current position in the buffer where writing begins. This value is updated to reflect the new
129+ /// position after writing.</param>
130+ /// <returns><see langword="true"/> if the value was successfully written to the buffer; otherwise, <see langword="false"/>.</returns>
85131 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
86132 public static bool TryWriteValue ( int variance , ref Span < char > buffer , ref int position ) {
87133 if ( buffer . Length < position + GetCharCount ( variance ) ) {
@@ -105,26 +151,44 @@ public static bool TryWriteValue(int variance, ref Span<char> buffer, ref int po
105151 }
106152
107153 /// <summary>
108- /// Normalizes a double value into an integer representation suitable for polyline encoding .
154+ /// Normalizes a given numeric value based on the specified type and precision settings .
109155 /// </summary>
110- /// <param name="value">
111- /// The double value to normalize.
112- /// </param>
113- /// <returns>
114- /// The normalized integer value.
115- /// </returns>
156+ /// <remarks>This method validates the input value to ensure it is finite and within the acceptable range
157+ /// for the specified type. If the value is valid, it applies a normalization algorithm using a predefined precision
158+ /// factor.</remarks>
159+ /// <param name="value">The numeric value to normalize. Must be a finite number.</param>
160+ /// <param name="type">The type against which the value is validated. Determines the acceptable range for the value.</param>
161+ /// <returns>An integer representing the normalized value. Returns <c>0</c> if the input value is <c>0.0</c>.</returns>
162+ /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is not a finite number or is outside the valid range for the specified
163+ /// <paramref name="type"/>.</exception>
116164 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
117- public static int Normalize ( double value ) => ( int ) Math . Round ( value * Defaults . Algorithm . Precision ) ;
165+ public static int Normalize ( double value , ValueType type ) {
166+ if ( double . IsNaN ( value ) || double . IsInfinity ( value ) ) {
167+ throw new ArgumentOutOfRangeException ( nameof ( value ) , "Value must be a finite number." ) ;
168+ }
169+
170+ if ( ! ValidateDenormalizedValue ( value , type ) ) {
171+ throw new ArgumentOutOfRangeException ( nameof ( value ) , value , "Value is out of range for the specified type." ) ;
172+ }
173+
174+ if ( value == 0.0 ) {
175+ return 0 ;
176+ }
177+
178+ return ( int ) Math . Round ( value * Defaults . Algorithm . Precision ) ;
179+ }
118180
119181 /// <summary>
120- /// Calculates the number of characters required to encode a given variance value.
182+ /// Determines the number of characters required to represent the specified integer value within predefined
183+ /// variance ranges.
121184 /// </summary>
122- /// <param name="variance">
123- /// The variance value to encode.
124- /// </param>
125- /// <returns>
126- /// The number of characters required to encode the variance.
127- /// </returns>
185+ /// <remarks>The method uses predefined ranges to efficiently determine the character count. Smaller
186+ /// values require fewer characters, while larger values require more. This method is optimized for performance
187+ /// using a switch expression.</remarks>
188+ /// <param name="variance">The integer value for which the character count is calculated. Must be within the range of a 32-bit signed
189+ /// integer.</param>
190+ /// <returns>The number of characters required to represent the <paramref name="variance"/> value, based on its magnitude.
191+ /// Returns a value between 1 and 6 inclusive.</returns>
128192 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
129193 public static int GetCharCount ( int variance ) => variance switch {
130194 // DO NOT CHANGE THE ORDER. We are skipping inside exclusive ranges as those are covered by previous statements.
@@ -135,4 +199,15 @@ public static bool TryWriteValue(int variance, ref Span<char> buffer, ref int po
135199 >= - 16777216 and <= + 16777215 => 5 ,
136200 _ => 6 ,
137201 } ;
202+
203+ /// <summary>
204+ /// Represents the type of a geographic coordinate value.
205+ /// </summary>
206+ /// <remarks>This enumeration is used to specify whether a coordinate value represents latitude or
207+ /// longitude. Latitude values indicate the north-south position, while longitude values indicate the east-west
208+ /// position.</remarks>
209+ public enum ValueType {
210+ Latitude ,
211+ Longitude
212+ }
138213}
0 commit comments