Skip to content

Commit 3ca7776

Browse files
committed
imporvements
1 parent d2190ce commit 3ca7776

9 files changed

Lines changed: 119 additions & 76 deletions

File tree

src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public IEnumerable<TCoordinate> Decode(TPolyline polyline) {
107107
InvalidPolylineException.Throw(position);
108108
}
109109

110-
yield return CreateCoordinate(PolylineEncoding.Denormalize(latitude, CoordinateValueType.Latitude), PolylineEncoding.Denormalize(longitude, CoordinateValueType.Longitude));
110+
yield return CreateCoordinate(PolylineEncoding.Denormalize(latitude, Options.Precision), PolylineEncoding.Denormalize(longitude, Options.Precision));
111111
}
112112

113113
_logger

src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ namespace PolylineAlgorithm.Abstraction;
1313
using System;
1414
using System.Buffers;
1515
using System.Diagnostics;
16-
using System.Globalization;
1716
using System.Runtime.CompilerServices;
1817

1918
/// <summary>
@@ -96,8 +95,8 @@ public TPolyline Encode(ReadOnlySpan<TCoordinate> coordinates) {
9695

9796
delta
9897
.Next(
99-
PolylineEncoding.Normalize(GetLatitude(coordinates[i]), CoordinateValueType.Latitude),
100-
PolylineEncoding.Normalize(GetLongitude(coordinates[i]), CoordinateValueType.Longitude)
98+
PolylineEncoding.Normalize(GetLatitude(coordinates[i]), Options.Precision),
99+
PolylineEncoding.Normalize(GetLongitude(coordinates[i]), Options.Precision)
101100
);
102101

103102
if (!PolylineEncoding.TryWriteValue(delta.Latitude, buffer, ref position)

src/PolylineAlgorithm/Coordinate.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@
55

66
namespace PolylineAlgorithm;
77

8-
using PolylineAlgorithm.Internal;
98
using PolylineAlgorithm.Properties;
109
using System;
1110
using System.Diagnostics;
1211
using System.Globalization;
1312
using System.Runtime.InteropServices;
14-
using System.Text.Json.Serialization;
1513
#if NET8_0_OR_GREATER
1614
using System.Text;
1715
using System.Linq.Expressions;

src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55

66
namespace PolylineAlgorithm.Extensions;
77

8-
using PolylineAlgorithm;
98
using PolylineAlgorithm.Abstraction;
109
using System;
1110
using System.Collections.Generic;
1211
using System.Runtime.InteropServices;
13-
using static PolylineAlgorithm.Internal.Defaults;
1412

1513
/// <summary>
1614
/// Provides extension methods for the <see cref="IPolylineEncoder{TCoordinate, TPolyline}"/> interface to facilitate encoding geographic coordinates into polylines.

src/PolylineAlgorithm/PolylineEncoding.cs

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ namespace PolylineAlgorithm;
88
using PolylineAlgorithm.Internal;
99
using PolylineAlgorithm.Properties;
1010
using System;
11-
using System.Globalization;
12-
#if NET8_0_OR_GREATER
13-
using System.Text;
14-
#endif
1511

1612
/// <summary>
1713
/// Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and de-normalizing
@@ -22,34 +18,13 @@ namespace PolylineAlgorithm;
2218
/// coordinates. It also provides validation utilities to ensure values conform to expected ranges for latitude and
2319
/// longitude.</remarks>
2420
public static class PolylineEncoding {
25-
#if NET8_0_OR_GREATER
26-
private static readonly CompositeFormat _argumentCannotBeCoordinateValueTypeMessageFormat = CompositeFormat.Parse(ExceptionMessageResource.ArgumentCannotBeCoordinateValueTypeMessageFormat);
27-
#else
28-
private static readonly string _argumentCannotBeCoordinateValueTypeMessageFormat = ExceptionMessageResource.ArgumentCannotBeCoordinateValueTypeMessageFormat;
29-
#endif
30-
31-
32-
/// <summary>
33-
/// Gets the midpoint rounding strategy used when normalizing coordinate values.
34-
/// </summary>
35-
/// <remarks>
36-
/// This property defines how rounding is performed during the normalization process in the <see cref="Normalize"/> method.
37-
/// The value is set to <see cref="MidpointRounding.AwayFromZero"/>, which means that when a number is halfway between two others,
38-
/// it is rounded toward the nearest number that is away from zero.
39-
/// </remarks>
40-
/// <value>
41-
/// <see cref="MidpointRounding.AwayFromZero"/>
42-
/// </value>
43-
public static MidpointRounding Rounding { get; } = MidpointRounding.AwayFromZero;
44-
4521
/// <summary>
4622
/// Normalizes a geographic coordinate value to an integer representation based on the specified precision.
4723
/// </summary>
4824
/// <remarks>
4925
/// <para>
5026
/// This method converts a floating-point coordinate value into a normalized integer by multiplying it by 10 raised
51-
/// to the power of the specified precision, then rounding the result using the <see cref="Rounding"/> strategy
52-
/// (<see cref="MidpointRounding.AwayFromZero"/>).
27+
/// to the power of the specified precision, then rounding the result using the specified <paramref name="rounding"/> strategy.
5328
/// </para>
5429
/// <para>
5530
/// For example, with the default precision of 5:
@@ -71,6 +46,10 @@ public static class PolylineEncoding {
7146
/// The value is multiplied by 10^<paramref name="precision"/> before rounding.
7247
/// Default is 5, which is standard for polyline encoding.
7348
/// </param>
49+
/// <param name="rounding">
50+
/// The rounding strategy to use when converting the scaled value to an integer.
51+
/// Default is <see cref="MidpointRounding.AwayFromZero"/>, which rounds midpoint values to the nearest number away from zero.
52+
/// </param>
7453
/// <returns>
7554
/// An integer representing the normalized value. Returns <c>0</c> if the input <paramref name="value"/> is <c>0.0</c>.
7655
/// </returns>
@@ -80,7 +59,7 @@ public static class PolylineEncoding {
8059
/// <exception cref="OverflowException">
8160
/// Thrown when the normalized result exceeds the range of a 32-bit signed integer during the conversion from double to int.
8261
/// </exception>
83-
public static int Normalize(double value, uint precision = 5) {
62+
public static int Normalize(double value, uint precision = 5, MidpointRounding rounding = MidpointRounding.AwayFromZero) {
8463
// Validate that the value is finite and not NaN or Infinity.
8564
if (!double.IsFinite(value)) {
8665
throw new ArgumentOutOfRangeException(nameof(value), ExceptionMessageResource.ArgumentValueMustBeFiniteNumber);
@@ -91,8 +70,10 @@ public static int Normalize(double value, uint precision = 5) {
9170
return 0;
9271
}
9372

73+
uint factor = Pow10.GetFactor(precision);
74+
9475
checked {
95-
return (int)Math.Round(precision == 0 ? value : value * Math.Pow(10, precision), Rounding);
76+
return (int)Math.Round(precision == 0 ? value : value * factor, rounding);
9677
}
9778
}
9879

@@ -102,11 +83,11 @@ public static int Normalize(double value, uint precision = 5) {
10283
/// <remarks>
10384
/// <para>
10485
/// This method reverses the normalization process performed by <see cref="Normalize"/>. It takes an integer value and converts it
105-
/// to a double by dividing by 10 raised to the power of the specified precision. If the precision is 0, the value is returned as a double
86+
/// to a double by dividing it by 10 raised to the power of the specified precision. If the precision is 0, the value is returned as a double
10687
/// without division.
10788
/// </para>
10889
/// <para>
109-
/// The calculation is performed inside a <c>checked</c> block to ensure that any arithmetic overflow is detected and an <see cref="OverflowException"/> is thrown.
90+
/// The calculation is performed inside a <see langword="checked"/> block to ensure that any arithmetic overflow is detected and an <see cref="OverflowException"/> is thrown.
11091
/// </para>
11192
/// <para>
11293
/// For example, with a precision of 5:
@@ -132,14 +113,14 @@ public static int Normalize(double value, uint precision = 5) {
132113
/// Thrown if the arithmetic operation overflows during conversion.
133114
/// </exception>
134115
public static double Denormalize(int value, uint precision = 5) {
135-
// Return fast if the value is zero, return 0.0 as the denormalized value.
136116
if (value == 0) {
137117
return 0.0;
138118
}
139119

140-
// Using 'checked' to ensure overflow exceptions are thrown if the result exceeds numeric limits.
120+
uint factor = Pow10.GetFactor(precision);
121+
141122
checked {
142-
return precision == 0 ? value : value / Math.Pow(10, precision);
123+
return precision == 0 ? value : (double)value / factor;
143124
}
144125
}
145126

@@ -242,7 +223,7 @@ public static bool TryReadValue(ref int delta, ReadOnlyMemory<char> buffer, ref
242223
/// </returns>
243224
public static bool TryWriteValue(int delta, Span<char> buffer, ref int position) {
244225
// Validate that the position and required space for write is within the bounds of the buffer.
245-
if (buffer.Slice(position).Length < GetRequiredBufferSize(delta)) {
226+
if (buffer[position..].Length < GetRequiredBufferSize(delta)) {
246227
return false;
247228
}
248229

src/PolylineAlgorithm/PolylineEncodingOptions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ public sealed class PolylineEncodingOptions {
3333
/// </remarks>
3434
public int StackAllocLimit { get; internal set; } = 512;
3535

36+
/// <summary>
37+
/// Gets the precision used for polyline encoding.
38+
/// </summary>
39+
/// <remarks>
40+
/// The precision determines the number of decimal places to use when encoding coordinates.
41+
/// The default value is 5, which is commonly used for geographic coordinates.
42+
/// </remarks>
43+
public uint Precision { get; internal set; } = 5;
44+
3645
/// <summary>
3746
/// Returns type name of a logger factory for debugging purposes.
3847
/// </summary>

src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public sealed class PolylineEncodingOptionsBuilder {
2222
#else
2323
private static readonly string _stackAllocLimitMustBeEqualOrGreaterThanMessageFormat = ExceptionMessageResource.StackAllocLimitMustBeEqualOrGreaterThanMessageFormat;
2424
#endif
25+
private uint _precision = 5;
2526
private int _stackAllocLimit = 512;
2627
private ILoggerFactory _loggerFactory = NullLoggerFactory.Instance;
2728

@@ -45,28 +46,50 @@ public static PolylineEncodingOptionsBuilder Create() {
4546
/// </returns>
4647
public PolylineEncodingOptions Build() {
4748
return new PolylineEncodingOptions {
49+
Precision = _precision,
4850
StackAllocLimit = _stackAllocLimit,
4951
LoggerFactory = _loggerFactory,
5052
};
5153
}
5254

5355
/// <summary>
54-
/// Sets the buffer size for encoding operations.
56+
/// Configures the buffer size used for stack allocation during polyline encoding operations.
5557
/// </summary>
5658
/// <param name="stackAllocLimit">
57-
/// The maximum buffer size. Must be greater than 11.
59+
/// The maximum buffer size to use for stack allocation. Must be greater than or equal to 1.
5860
/// </param>
5961
/// <returns>
60-
/// The current builder instance.
62+
/// Returns the current <see cref="PolylineEncodingOptionsBuilder"/> instance for method chaining.
6163
/// </returns>
62-
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="stackAllocLimit"/> is less than or equal to 11.</exception>
64+
/// <exception cref="ArgumentOutOfRangeException">
65+
/// Thrown if <paramref name="stackAllocLimit"/> is less than 1.
66+
/// </exception>
67+
/// <remarks>
68+
/// This method allows customization of the internal buffer size for encoding, which can impact performance and memory usage.
69+
/// </remarks>
6370
public PolylineEncodingOptionsBuilder WithStackAllocLimit(int stackAllocLimit) {
6471
const int minStackAllocLimit = 1;
72+
6573
_stackAllocLimit = stackAllocLimit >= minStackAllocLimit ? stackAllocLimit : throw new ArgumentOutOfRangeException(nameof(stackAllocLimit), string.Format(CultureInfo.InvariantCulture, _stackAllocLimitMustBeEqualOrGreaterThanMessageFormat, minStackAllocLimit));
6674

6775
return this;
6876
}
6977

78+
/// <summary>
79+
/// Sets the precision for encoding values.
80+
/// </summary>
81+
/// <param name="precision">
82+
/// The number of decimal places to use for encoding values. Default is 5.
83+
/// </param>
84+
/// <returns>
85+
/// The current builder instance.
86+
/// </returns>
87+
public PolylineEncodingOptionsBuilder WithPrecision(uint precision) {
88+
_precision = precision;
89+
90+
return this;
91+
}
92+
7093
/// <summary>
7194
/// Sets the logger factory for logging during encoding operations.
7295
/// </summary>

src/PolylineAlgorithm/Pow10.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
namespace PolylineAlgorithm;
2+
3+
using System;
4+
5+
/// <summary>
6+
/// Provides optimized calculation of powers of 10 for precision-based operations.
7+
/// </summary>
8+
/// <remarks>
9+
/// This class caches common powers of 10 (10^0 through 10^9) for efficient lookup,
10+
/// falling back to <see cref="Math.Pow"/> for larger exponents.
11+
/// </remarks>
12+
public static class Pow10 {
13+
/// <summary>
14+
/// Pre-computed powers of 10 from 10^0 to 10^9.
15+
/// </summary>
16+
private static readonly uint[] _pow10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
17+
18+
/// <summary>
19+
/// Gets the multiplication factor for a given precision level.
20+
/// </summary>
21+
/// <param name="precision">The precision level (exponent for base 10).</param>
22+
/// <returns>
23+
/// The power of 10 corresponding to the specified precision (10^precision).
24+
/// </returns>
25+
/// <remarks>
26+
/// For precision values 0-9, returns pre-computed values for optimal performance.
27+
/// For larger values, computes the result using <see cref="Math.Pow"/>.
28+
/// The operation is performed in a checked context to detect arithmetic overflow.
29+
/// </remarks>
30+
/// <exception cref="OverflowException">
31+
/// Thrown when the computed power of 10 exceeds <see cref="uint.MaxValue"/>.
32+
/// </exception>
33+
public static uint GetFactor(uint precision) {
34+
checked {
35+
return precision < _pow10.Length ? _pow10[precision] : (uint)Math.Pow(10, precision);
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)