Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 63 additions & 19 deletions src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@

namespace PolylineAlgorithm.Abstraction {
/// <summary>
/// Decodes encoded polyline strings into sequences of geographic coordinates.
/// Implements the <see cref="IPolylineDecoder{TPolyline, TCoordinate}"/> interface.
/// Provides a base implementation for decoding encoded polyline strings into sequences of geographic coordinates.
/// </summary>
/// <remarks>
/// This abstract class provides a base implementation for decoding polylines, allowing subclasses to define how to handle specific polyline formats.
/// Derive from this class to implement a decoder for a specific polyline type. Override <see cref="GetReadOnlyMemory"/>
/// and <see cref="CreateCoordinate"/> to provide type-specific behavior.
/// </remarks>
/// <typeparam name="TPolyline">The type that represents the encoded polyline input.</typeparam>
/// <typeparam name="TCoordinate">The type that represents a decoded geographic coordinate.</typeparam>
public abstract class AbstractPolylineDecoder<TPolyline, TCoordinate> : IPolylineDecoder<TPolyline, TCoordinate> {
private readonly ILogger<AbstractPolylineDecoder<TPolyline, TCoordinate>> _logger;

Expand All @@ -34,7 +36,7 @@ protected AbstractPolylineDecoder()
/// The <see cref="PolylineEncodingOptions"/> to use for encoding operations.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="options"/> is <see langword="null" />
/// Thrown when <paramref name="options"/> is <see langword="null"/>.
/// </exception>
protected AbstractPolylineDecoder(PolylineEncodingOptions options) {
if (options is null) {
Expand Down Expand Up @@ -74,14 +76,30 @@ public IEnumerable<TCoordinate> Decode(TPolyline polyline)
=> Decode(polyline, CancellationToken.None);

/// <summary>
/// Decodes an encoded polyline with cancellation support.
/// Decodes an encoded <typeparamref name="TPolyline"/> into a sequence of <typeparamref name="TCoordinate"/> instances,
/// with support for cancellation.
/// </summary>
/// <param name="polyline">The encoded polyline.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Decoded coordinates.</returns>
/// <exception cref="ArgumentNullException"/>
/// <exception cref="ArgumentException"/>
/// <exception cref="InvalidPolylineException"/>
/// <param name="polyline">
/// The <typeparamref name="TPolyline"/> instance containing the encoded polyline string to decode.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the decoding operation.
/// </param>
/// <returns>
/// An <see cref="IEnumerable{T}"/> of <typeparamref name="TCoordinate"/> representing the decoded latitude and longitude pairs.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="polyline"/> is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="polyline"/> is empty.
/// </exception>
/// <exception cref="InvalidPolylineException">
/// Thrown when the polyline format is invalid or malformed at a specific position.
/// </exception>
/// <exception cref="OperationCanceledException">
/// Thrown when <paramref name="cancellationToken"/> is canceled during decoding.
/// </exception>
public IEnumerable<TCoordinate> Decode(TPolyline polyline, CancellationToken cancellationToken) {
const string OperationName = nameof(Decode);

Expand Down Expand Up @@ -124,11 +142,9 @@ public IEnumerable<TCoordinate> Decode(TPolyline polyline, CancellationToken can

/// <summary>
/// Validates that the provided polyline is not <see langword="null"/>.
/// Throws an <see cref="ArgumentNullException"/> if the polyline is <see langword="null"/>.
/// Optionally logs a warning if a logger is provided.
/// </summary>
/// <param name="polyline">The polyline instance to validate.</param>
/// <param name="logger">Optional logger for diagnostic messages.</param>
/// <param name="logger">An optional <see cref="ILogger"/> used to log a warning when validation fails.</param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="polyline"/> is <see langword="null"/>.
/// </exception>
Expand All @@ -141,12 +157,10 @@ private static void ValidateNullPolyline(TPolyline polyline, ILogger? logger) {
}

/// <summary>
/// Validates that the polyline sequence meets the minimum required length.
/// Throws an <see cref="InvalidPolylineException"/> if the sequence is too short.
/// Optionally logs diagnostic messages if a logger is provided.
/// Validates that the polyline character sequence meets the minimum required length.
/// </summary>
/// <param name="polylineSequence">The polyline character sequence to validate.</param>
/// <param name="logger">Optional logger for diagnostic messages.</param>
/// <param name="logger">An optional <see cref="ILogger"/> used to log diagnostic messages when validation fails.</param>
/// <exception cref="InvalidPolylineException">
/// Thrown when <paramref name="polylineSequence"/> is shorter than the minimum allowed length.
/// </exception>
Expand All @@ -161,8 +175,17 @@ private static void ValidateSequence(ReadOnlyMemory<char> polylineSequence, ILog
}

/// <summary>
/// Validates the polyline format for allowed characters.
/// Validates the format of the polyline character sequence, ensuring all characters are within the allowed range.
/// </summary>
/// <param name="sequence">
/// The read-only memory region of characters representing the polyline to validate.
/// </param>
/// <param name="logger">
/// An optional <see cref="ILogger"/> used to log a warning when format validation fails.
/// </param>
/// <exception cref="ArgumentException">
/// Thrown when the polyline contains characters outside the valid encoding range or has an invalid block structure.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void ValidateFormat(ReadOnlyMemory<char> sequence, ILogger? logger) {
try {
Expand All @@ -174,9 +197,30 @@ protected virtual void ValidateFormat(ReadOnlyMemory<char> sequence, ILogger? lo
}
}

/// <summary>
/// Extracts the underlying read-only memory region of characters from the specified polyline instance.
/// </summary>
/// <param name="polyline">
/// The <typeparamref name="TPolyline"/> instance from which to extract the character sequence.
/// </param>
/// <returns>
/// A <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> representing the encoded polyline characters.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected abstract ReadOnlyMemory<char> GetReadOnlyMemory(in TPolyline polyline);

/// <summary>
/// Creates a <typeparamref name="TCoordinate"/> instance from the specified latitude and longitude values.
/// </summary>
/// <param name="latitude">
/// The latitude component of the coordinate, in degrees.
/// </param>
/// <param name="longitude">
/// The longitude component of the coordinate, in degrees.
/// </param>
/// <returns>
/// A <typeparamref name="TCoordinate"/> instance representing the specified geographic coordinate.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected abstract TCoordinate CreateCoordinate(double latitude, double longitude);
}
Expand Down
13 changes: 8 additions & 5 deletions src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ namespace PolylineAlgorithm.Abstraction;
using System.Runtime.CompilerServices;

/// <summary>
/// Provides functionality to encode a collection of geographic coordinates into an encoded polyline string.
/// Implements the <see cref="IPolylineEncoder{TCoordinate, TPolyline}"/> interface.
/// Provides a base implementation for encoding sequences of geographic coordinates into encoded polyline strings.
/// </summary>
/// <remarks>
/// This abstract class serves as a base for specific polyline encoders, allowing customization of the encoding process.
/// Derive from this class to implement an encoder for a specific coordinate and polyline type. Override
/// <see cref="GetLatitude"/>, <see cref="GetLongitude"/>, and <see cref="CreatePolyline"/> to provide type-specific behavior.
/// </remarks>
/// <typeparam name="TCoordinate">The type that represents a geographic coordinate to encode.</typeparam>
/// <typeparam name="TPolyline">The type that represents the encoded polyline output.</typeparam>
public abstract class AbstractPolylineEncoder<TCoordinate, TPolyline> : IPolylineEncoder<TCoordinate, TPolyline> {
private readonly ILogger<AbstractPolylineEncoder<TCoordinate, TPolyline>> _logger;
/// <summary>
Expand Down Expand Up @@ -68,8 +70,9 @@ protected AbstractPolylineEncoder(PolylineEncodingOptions options) {
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="coordinates"/> is an empty enumeration.
/// </exception>
/// <exception cref="InternalBufferOverflowException"></exception>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="InvalidOperationException">
/// Thrown when the internal encoding buffer cannot accommodate the encoded value.
/// </exception>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains local methods. Actual method only 55 lines.")]
public TPolyline Encode(ReadOnlySpan<TCoordinate> coordinates) {
const string OperationName = nameof(Encode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static class PolylineDecoderExtensions {
/// An <see cref="IEnumerable{Coordinate}"/> containing the decoded latitude and longitude pairs.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="decoder"/> is <see langword="null"/>.
/// Thrown when <paramref name="decoder"/> or <paramref name="polyline"/> is <see langword="null"/>.
/// </exception>
public static IEnumerable<Coordinate> Decode(this IPolylineDecoder<Polyline, Coordinate> decoder, string polyline) {
if (decoder is null) {
Expand All @@ -51,7 +51,7 @@ public static IEnumerable<Coordinate> Decode(this IPolylineDecoder<Polyline, Coo
/// An <see cref="IEnumerable{Coordinate}"/> containing the decoded latitude and longitude pairs.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="decoder"/> is <see langword="null"/>.
/// Thrown when <paramref name="decoder"/> or <paramref name="polyline"/> is <see langword="null"/>.
/// </exception>
public static IEnumerable<Coordinate> Decode(this IPolylineDecoder<Polyline, Coordinate> decoder, char[] polyline) {
if (decoder is null) {
Expand Down
25 changes: 16 additions & 9 deletions src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@

//
// Copyright © Pete Sramek. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//

namespace PolylineAlgorithm.Extensions;

Expand All @@ -15,19 +18,21 @@ namespace PolylineAlgorithm.Extensions;
/// </summary>
public static class PolylineEncoderExtensions {
/// <summary>
/// Encodes a collection of <see cref="PolylineAlgorithm.Coordinate"/> instances into an encoded polyline.
/// Encodes a <see cref="List{T}"/> of <typeparamref name="TCoordinate"/> instances into an encoded polyline.
/// </summary>
/// <typeparam name="TCoordinate">The type that represents a geographic coordinate to encode.</typeparam>
/// <typeparam name="TPolyline">The type that represents the encoded polyline output.</typeparam>
/// <param name="encoder">
/// The <see cref="IPolylineEncoder{TCoordinate, TPolyline}"/> instance used to perform the encoding operation.
/// </param>
/// <param name="coordinates">
/// The sequence of <see cref="PolylineAlgorithm.Coordinate"/> objects to encode.
/// The list of <typeparamref name="TCoordinate"/> objects to encode.
/// </param>
/// <returns>
/// A <see cref="PolylineAlgorithm.Polyline"/> representing the encoded polyline string for the provided coordinates.
/// A <typeparamref name="TPolyline"/> instance representing the encoded polyline for the provided coordinates.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="encoder"/> is <see langword="null"/>.
/// Thrown when <paramref name="encoder"/> or <paramref name="coordinates"/> is <see langword="null"/>.
/// </exception>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "We need a list as we do need to marshal it as span.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "MA0016:Prefer using collection abstraction instead of implementation", Justification = "We need a list as we do need to marshal it as span.")]
Expand All @@ -49,19 +54,21 @@ public static TPolyline Encode<TCoordinate, TPolyline>(this IPolylineEncoder<TCo


/// <summary>
/// Encodes an array of <see cref="PolylineAlgorithm.Coordinate"/> instances into an encoded polyline.
/// Encodes an array of <typeparamref name="TCoordinate"/> instances into an encoded polyline.
/// </summary>
/// <typeparam name="TCoordinate">The type that represents a geographic coordinate to encode.</typeparam>
/// <typeparam name="TPolyline">The type that represents the encoded polyline output.</typeparam>
/// <param name="encoder">
/// The <see cref="IPolylineEncoder{TCoordinate, TPolyline}"/> instance used to perform the encoding operation.
/// </param>
/// <param name="coordinates">
/// The array of <see cref="PolylineAlgorithm.Coordinate"/> objects to encode.
/// The array of <typeparamref name="TCoordinate"/> objects to encode.
/// </param>
/// <returns>
/// A <see cref="PolylineAlgorithm.Polyline"/> representing the encoded polyline string for the provided coordinates.
/// A <typeparamref name="TPolyline"/> instance representing the encoded polyline for the provided coordinates.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="encoder"/> is <see langword="null"/>.
/// Thrown when <paramref name="encoder"/> or <paramref name="coordinates"/> is <see langword="null"/>.
/// </exception>
public static TPolyline Encode<TCoordinate, TPolyline>(this IPolylineEncoder<TCoordinate, TPolyline> encoder, TCoordinate[] coordinates) {
if (encoder is null) {
Expand Down
6 changes: 3 additions & 3 deletions src/PolylineAlgorithm/Internal/CoordinateDelta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ namespace PolylineAlgorithm.Internal;

/// <summary>
/// Represents the difference (delta) in latitude and longitude between consecutive geographic coordinates.
/// This struct is used to compute and store the change in coordinate values as integer deltas.
/// </summary>
/// <remarks>
/// This struct computes and stores the change in coordinate values as integer deltas between successive coordinates.
/// </remarks>
[DebuggerDisplay("{ToString(),nq}")]
[StructLayout(LayoutKind.Auto)]
internal struct CoordinateDelta {
Expand Down Expand Up @@ -39,7 +41,6 @@ public CoordinateDelta() {
/// </summary>
/// <param name="latitude">The next latitude value.</param>
/// <param name="longitude">The next longitude value.</param>

public void Next(int latitude, int longitude) {
Latitude = Delta(_current.Latitude, latitude);
Longitude = Delta(_current.Longitude, longitude);
Expand All @@ -57,7 +58,6 @@ public void Next(int latitude, int longitude) {
/// <param name="initial">The previous coordinate value.</param>
/// <param name="next">The next coordinate value.</param>
/// <returns>The computed delta between <paramref name="initial"/> and <paramref name="next"/>.</returns>

private static int Delta(int initial, int next) => next - initial;

/// <summary>
Expand Down
11 changes: 8 additions & 3 deletions src/PolylineAlgorithm/Internal/Defaults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ namespace PolylineAlgorithm.Internal;

/// <summary>
/// Provides default values and constants used throughout the Polyline Algorithm.
/// Organizes defaults for algorithm parameters, polyline encoding, and geographic coordinates into nested static classes.
/// </summary>
/// <remarks>
/// Organizes defaults for algorithm parameters, polyline encoding, and geographic coordinates into nested static classes.
/// </remarks>
[ExcludeFromCodeCoverage]
internal static class Defaults {
/// <summary>
/// Contains default values and constants specific to the polyline encoding logging.
/// </summary>
internal static class Logging {
/// <summary>
/// Log level multiplier used to distinguish event identification for each log level
/// Log level multiplier used to distinguish event identification for each log level.
/// </summary>
internal const int LogLevelMultiplier = 100;
}
Expand Down Expand Up @@ -48,6 +50,9 @@ internal static class Algorithm {
internal const byte UnitSeparator = 31;
}

/// <summary>
/// Contains default values and constants for geographic coordinate validation.
/// </summary>
internal static class Coordinate {
/// <summary>
/// Provides constants representing latitude values, including the default, minimum, and maximum valid values.
Expand Down Expand Up @@ -95,7 +100,7 @@ internal static class Polyline {
/// </summary>
internal static class Block {
/// <summary>
/// Contains constants related to the length of encoded vakues in polyline encoding.
/// Contains constants related to the length of encoded values in polyline encoding.
/// </summary>
internal static class Length {
/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/PolylineAlgorithm/PolylineEncoding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static class PolylineEncoding {
/// <remarks>
/// <para>
/// This method converts a floating-point coordinate value into a normalized integer by multiplying it by 10 raised
/// to the power of the specified precision, then rounding the result using the specified <paramref name="rounding"/> strategy.
/// to the power of the specified precision, then truncating the result to an integer.
/// </para>
/// <para>
/// For example, with the default precision of 5:
Expand Down Expand Up @@ -211,7 +211,7 @@ public static bool TryReadValue(ref int delta, ReadOnlyMemory<char> buffer, ref
/// more characters follow. The position is advanced as characters are written.
/// </para>
/// <para>
/// Before writing, the method validates that sufficient space is available in the buffer by calling <see cref="GetCharCount"/>.
/// Before writing, the method validates that sufficient space is available in the buffer by calling <see cref="GetRequiredBufferSize"/>.
/// If the buffer does not have enough remaining capacity, the method returns <see langword="false"/> without modifying the buffer or position.
/// </para>
/// <para>
Expand Down
Loading
Loading