From ee09102723c00a888c1db28b5b497e1c02abde2e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:30:22 +0000 Subject: [PATCH 01/22] feat: add PolylineFormatter, FormatterBuilder, PolylineOptions and FormatterRule Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/16db7628-2cc2-4c51-982b-0264a04d7157 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- src/PolylineAlgorithm/FormatterBuilder.cs | 119 +++++ .../Internal/FormatterRule.cs | 53 ++ src/PolylineAlgorithm/PolylineFormatter.cs | 80 +++ src/PolylineAlgorithm/PolylineOptions.cs | 50 ++ src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 14 + .../PolylineFormatterTests.cs | 469 ++++++++++++++++++ 6 files changed, 785 insertions(+) create mode 100644 src/PolylineAlgorithm/FormatterBuilder.cs create mode 100644 src/PolylineAlgorithm/Internal/FormatterRule.cs create mode 100644 src/PolylineAlgorithm/PolylineFormatter.cs create mode 100644 src/PolylineAlgorithm/PolylineOptions.cs create mode 100644 tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs diff --git a/src/PolylineAlgorithm/FormatterBuilder.cs b/src/PolylineAlgorithm/FormatterBuilder.cs new file mode 100644 index 00000000..26021eaf --- /dev/null +++ b/src/PolylineAlgorithm/FormatterBuilder.cs @@ -0,0 +1,119 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +using PolylineAlgorithm.Internal; +using System; +using System.Collections.Generic; + +/// +/// Provides a fluent builder for constructing a . +/// +/// The source object type from which column values are extracted. +/// +/// +/// Use to obtain an instance, call once per column, +/// optionally chain to specify an epoch for the most-recently added column, +/// then call to produce the immutable . +/// +/// +/// The builder is the only way to create a — its +/// constructor is internal. +/// +/// +public sealed class FormatterBuilder { + private readonly List> _rules = []; + private readonly HashSet _names = new(StringComparer.Ordinal); + + private FormatterBuilder() { } + + /// + /// Creates a new instance. + /// + /// A fresh with no rules. + public static FormatterBuilder Create() => new(); + + /// + /// Adds a column with the specified value selector and precision. + /// + /// + /// A unique, non-null, non-empty name that identifies the column. Used for diagnostics only. + /// + /// + /// A delegate that extracts the column's raw value from an item of type + /// . + /// + /// + /// The number of decimal places to preserve. Each extracted value is multiplied by + /// 10^ before encoding. Defaults to 5. + /// + /// The current instance for method chaining. + /// + /// Thrown when or is . + /// + /// + /// Thrown when is empty, or a rule with the same name already exists. + /// + public FormatterBuilder AddValue(string name, Func selector, uint precision = 5) { + if (name is null) { + throw new ArgumentNullException(nameof(name)); + } + + if (name.Length == 0) { + throw new ArgumentException("Name cannot be empty.", nameof(name)); + } + + if (selector is null) { + throw new ArgumentNullException(nameof(selector)); + } + + if (!_names.Add(name)) { + throw new ArgumentException($"A rule with the name '{name}' has already been added.", nameof(name)); + } + + _rules.Add(new FormatterRule(name, (long)Pow10.GetFactor(precision), selector)); + + return this; + } + + /// + /// Sets a baseline (epoch) on the most-recently added column. + /// During encoding the baseline is subtracted from the first item's scaled column value, + /// keeping the initial delta small when the absolute first value is large. + /// + /// The baseline value to apply to the first item's column value. + /// The current instance for method chaining. + /// + /// Thrown when no rules have been added yet. Call before . + /// + public FormatterBuilder SetBaseline(long baseline) { + if (_rules.Count == 0) { + throw new InvalidOperationException("Cannot set a baseline when no rules have been added. Call AddValue first."); + } + + var last = _rules[^1]; + _rules[^1] = new FormatterRule(last.Name, last.Factor, last.Select, baseline); + + return this; + } + + /// + /// Bakes all added rules into a sealed, immutable . + /// + /// + /// An immutable whose rules can no longer be changed. + /// + /// + /// Thrown when no rules have been added. + /// + public PolylineFormatter Build() { + if (_rules.Count == 0) { + throw new InvalidOperationException("At least one rule must be added before calling Build."); + } + + return new PolylineFormatter(_rules.ToArray()); + } +} diff --git a/src/PolylineAlgorithm/Internal/FormatterRule.cs b/src/PolylineAlgorithm/Internal/FormatterRule.cs new file mode 100644 index 00000000..9d5a4ab8 --- /dev/null +++ b/src/PolylineAlgorithm/Internal/FormatterRule.cs @@ -0,0 +1,53 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm.Internal; + +using System; + +/// +/// Represents a single column rule baked into a . +/// Stores the pre-calculated factor and an optional baseline alongside the user-supplied value selector. +/// +/// The source object type from which the column value is extracted. +internal sealed class FormatterRule { + /// + /// Initializes a new instance of . + /// + /// The column name used for diagnostics. + /// The pre-calculated scaling factor (10^precision). + /// The delegate that extracts the raw value from an item. + /// The optional baseline value applied to the first item only. + internal FormatterRule(string name, long factor, Func select, long? baseline = null) { + Name = name; + Factor = factor; + Select = select; + Baseline = baseline; + } + + /// + /// Gets the column name. Used for diagnostics and duplicate-name detection only. + /// + internal string Name { get; } + + /// + /// Gets the pre-calculated scaling factor (10^precision). + /// Stored as so that (long)(value * Factor) stays in 64-bit arithmetic + /// throughout the encoding hot loop without additional casting. + /// + internal long Factor { get; } + + /// + /// Gets the optional baseline (epoch). When set, the encoder subtracts this value from the + /// first point's scaled column value to keep the initial delta small. + /// + internal long? Baseline { get; } + + /// + /// Gets the delegate that extracts the column's raw value from an item of type + /// . Stored as a concrete delegate so the JIT can inline the call site. + /// + internal Func Select { get; } +} diff --git a/src/PolylineAlgorithm/PolylineFormatter.cs b/src/PolylineAlgorithm/PolylineFormatter.cs new file mode 100644 index 00000000..b290115c --- /dev/null +++ b/src/PolylineAlgorithm/PolylineFormatter.cs @@ -0,0 +1,80 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +using PolylineAlgorithm.Internal; +using System; +using System.Runtime.CompilerServices; + +/// +/// Provides an immutable, sealed rule engine that describes how to extract and scale values from +/// an object of type for polyline encoding. +/// +/// The source object type from which column values are extracted. +/// +/// +/// Instances of this class are constructed exclusively through . +/// +/// +/// The modifier allows the JIT to devirtualise and inline calls to +/// , eliminating vtable dispatch in the encoding hot loop. +/// +/// +public sealed class PolylineFormatter { + private readonly FormatterRule[] _rules; + + /// + /// Initializes a new instance of with the baked rules. + /// This constructor is intentionally internal; use to create instances. + /// + /// The pre-calculated rules array produced by the builder. + internal PolylineFormatter(FormatterRule[] rules) { + _rules = rules; + Width = rules.Length; + HasBaselines = Array.Exists(rules, static r => r.Baseline.HasValue); + } + + /// + /// Gets the number of columns (values per item). + /// This is the required length of the passed to . + /// + public int Width { get; } + + /// + /// Gets a value indicating whether any column has a baseline defined. + /// When the encoder can skip the baseline-subtraction branch entirely, + /// keeping the common-case encoding path branch-free. + /// + public bool HasBaselines { get; } + + /// + /// Extracts and scales all column values from into the span. + /// Called once per item in the encoding hot loop. This method performs no heap allocation; + /// the caller is responsible for providing and owning the output buffer. + /// + /// The source item from which column values are extracted. + /// + /// Output buffer that receives the scaled values. + /// Its length must equal . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GetValues(T item, Span values) { + var rules = _rules; // local copy avoids repeated bounds check on the field + for (var i = 0; i < rules.Length; i++) { + ref var rule = ref rules[i]; + values[i] = (long)(rule.Select(item) * rule.Factor); + } + } + + /// + /// Returns the baseline for the column at , or 0 if none is configured. + /// The encoder subtracts this value from the first item's scaled column value during encoding. + /// + /// The zero-based column index. Must be in the range [0, Width). + /// The baseline value, or 0 when no baseline has been defined for the column. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long GetBaseline(int index) => _rules[index].Baseline ?? 0L; +} diff --git a/src/PolylineAlgorithm/PolylineOptions.cs b/src/PolylineAlgorithm/PolylineOptions.cs new file mode 100644 index 00000000..f5c38ff7 --- /dev/null +++ b/src/PolylineAlgorithm/PolylineOptions.cs @@ -0,0 +1,50 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +using System; + +/// +/// Provides configuration for a -driven encoding operation. +/// +/// The source object type from which column values are extracted. +/// +/// Combines a (which defines the column schema and scaling rules) +/// with a (which controls buffer sizes, precision, and logging). +/// +public sealed class PolylineOptions { + /// + /// Initializes a new instance of . + /// + /// + /// The sealed formatter that defines the column schema. Must not be . + /// + /// + /// The encoding options that control buffer sizes, precision, and logging. + /// Pass to use default options. + /// + /// + /// Thrown when is . + /// + public PolylineOptions(PolylineFormatter formatter, PolylineEncodingOptions? encoding = null) { + if (formatter is null) { + throw new ArgumentNullException(nameof(formatter)); + } + + Formatter = formatter; + Encoding = encoding ?? new PolylineEncodingOptions(); + } + + /// + /// Gets the sealed formatter that defines the column schema and scaling rules. + /// + public PolylineFormatter Formatter { get; } + + /// + /// Gets the encoding options that control buffer sizes, precision, and logging. + /// + public PolylineEncodingOptions Encoding { get; } +} diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index 7dc5c581..32a0147c 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -1 +1,15 @@ #nullable enable +PolylineAlgorithm.FormatterBuilder +PolylineAlgorithm.FormatterBuilder.AddValue(string! name, System.Func! selector, uint precision = 5) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.FormatterBuilder.Build() -> PolylineAlgorithm.PolylineFormatter! +PolylineAlgorithm.FormatterBuilder.SetBaseline(long baseline) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.PolylineFormatter +PolylineAlgorithm.PolylineFormatter.GetBaseline(int index) -> long +PolylineAlgorithm.PolylineFormatter.GetValues(T item, System.Span values) -> void +PolylineAlgorithm.PolylineFormatter.HasBaselines.get -> bool +PolylineAlgorithm.PolylineFormatter.Width.get -> int +PolylineAlgorithm.PolylineOptions +PolylineAlgorithm.PolylineOptions.Encoding.get -> PolylineAlgorithm.PolylineEncodingOptions! +PolylineAlgorithm.PolylineOptions.Formatter.get -> PolylineAlgorithm.PolylineFormatter! +PolylineAlgorithm.PolylineOptions.PolylineOptions(PolylineAlgorithm.PolylineFormatter! formatter, PolylineAlgorithm.PolylineEncodingOptions? encoding = null) -> void +static PolylineAlgorithm.FormatterBuilder.Create() -> PolylineAlgorithm.FormatterBuilder! diff --git a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs new file mode 100644 index 00000000..5102e23f --- /dev/null +++ b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs @@ -0,0 +1,469 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm.Tests; + +using PolylineAlgorithm; +using System; + +/// +/// Tests for , , +/// and . +/// +[TestClass] +public sealed class PolylineFormatterTests { + // --------------------------------------------------------------------------- + // FormatterBuilder.Create + // --------------------------------------------------------------------------- + + [TestMethod] + public void Create_Returns_New_Builder() { + // Act + FormatterBuilder<(double X, double Y)> result = FormatterBuilder<(double X, double Y)>.Create(); + + // Assert + Assert.IsNotNull(result); + } + + [TestMethod] + public void Create_With_Multiple_Invocations_Returns_Different_Instances() { + // Act + FormatterBuilder<(double X, double Y)> first = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y)> second = FormatterBuilder<(double X, double Y)>.Create(); + + // Assert + Assert.AreNotSame(first, second); + } + + // --------------------------------------------------------------------------- + // FormatterBuilder.AddValue — argument validation + // --------------------------------------------------------------------------- + + [TestMethod] + public void AddValue_With_Null_Name_Throws_ArgumentNullException() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => builder.AddValue(null!, static t => t.X)); + Assert.AreEqual("name", ex.ParamName); + } + + [TestMethod] + public void AddValue_With_Empty_Name_Throws_ArgumentException() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + + // Act & Assert + ArgumentException ex = Assert.ThrowsExactly( + () => builder.AddValue(string.Empty, static t => t.X)); + Assert.AreEqual("name", ex.ParamName); + } + + [TestMethod] + public void AddValue_With_Null_Selector_Throws_ArgumentNullException() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => builder.AddValue("X", null!)); + Assert.AreEqual("selector", ex.ParamName); + } + + [TestMethod] + public void AddValue_With_Duplicate_Name_Throws_ArgumentException() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + builder.AddValue("X", static t => t.X); + + // Act & Assert + ArgumentException ex = Assert.ThrowsExactly( + () => builder.AddValue("X", static t => t.Y)); + Assert.AreEqual("name", ex.ParamName); + } + + // --------------------------------------------------------------------------- + // FormatterBuilder.AddValue — happy path & chaining + // --------------------------------------------------------------------------- + + [TestMethod] + public void AddValue_Returns_Same_Builder_For_Method_Chaining() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + + // Act + FormatterBuilder<(double X, double Y)> result = builder.AddValue("X", static t => t.X); + + // Assert + Assert.AreSame(builder, result); + } + + [TestMethod] + public void AddValue_With_Different_Names_Succeeds() { + // Arrange & Act + PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X) + .AddValue("Y", static t => t.Y) + .Build(); + + // Assert + Assert.AreEqual(2, formatter.Width); + } + + // --------------------------------------------------------------------------- + // FormatterBuilder.SetBaseline — argument validation + // --------------------------------------------------------------------------- + + [TestMethod] + public void SetBaseline_With_No_Rules_Throws_InvalidOperationException() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + + // Act & Assert + Assert.ThrowsExactly(() => builder.SetBaseline(1000L)); + } + + // --------------------------------------------------------------------------- + // FormatterBuilder.SetBaseline — happy path + // --------------------------------------------------------------------------- + + [TestMethod] + public void SetBaseline_Returns_Same_Builder_For_Method_Chaining() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X); + + // Act + FormatterBuilder<(double X, double Y)> result = builder.SetBaseline(100L); + + // Assert + Assert.AreSame(builder, result); + } + + [TestMethod] + public void SetBaseline_Applies_Only_To_Last_Added_Rule() { + // Arrange & Act + PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X) + .AddValue("Y", static t => t.Y) + .SetBaseline(500L) + .Build(); + + // Assert — only Y (index 1) has a baseline; X (index 0) returns 0 + Assert.AreEqual(0L, formatter.GetBaseline(0)); + Assert.AreEqual(500L, formatter.GetBaseline(1)); + } + + [TestMethod] + public void SetBaseline_Can_Be_Called_On_Each_Rule() { + // Arrange & Act + PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X).SetBaseline(100L) + .AddValue("Y", static t => t.Y).SetBaseline(200L) + .Build(); + + // Assert + Assert.AreEqual(100L, formatter.GetBaseline(0)); + Assert.AreEqual(200L, formatter.GetBaseline(1)); + } + + // --------------------------------------------------------------------------- + // FormatterBuilder.Build — validation + // --------------------------------------------------------------------------- + + [TestMethod] + public void Build_With_No_Rules_Throws_InvalidOperationException() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + + // Act & Assert + Assert.ThrowsExactly(() => builder.Build()); + } + + [TestMethod] + public void Build_With_Multiple_Invocations_Returns_Different_Instances() { + // Arrange + FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X); + + // Act + PolylineFormatter<(double X, double Y)> first = builder.Build(); + PolylineFormatter<(double X, double Y)> second = builder.Build(); + + // Assert + Assert.AreNotSame(first, second); + } + + // --------------------------------------------------------------------------- + // PolylineFormatter.Width + // --------------------------------------------------------------------------- + + [TestMethod] + public void Width_Equals_Number_Of_Added_Rules() { + // Arrange & Act + PolylineFormatter<(double X, double Y, double Z)> formatter = FormatterBuilder<(double X, double Y, double Z)>.Create() + .AddValue("X", static t => t.X) + .AddValue("Y", static t => t.Y) + .AddValue("Z", static t => t.Z) + .Build(); + + // Assert + Assert.AreEqual(3, formatter.Width); + } + + [TestMethod] + public void Width_Is_One_For_Single_Rule() { + // Arrange & Act + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .Build(); + + // Assert + Assert.AreEqual(1, formatter.Width); + } + + // --------------------------------------------------------------------------- + // PolylineFormatter.HasBaselines + // --------------------------------------------------------------------------- + + [TestMethod] + public void HasBaselines_Is_False_When_No_Baselines_Are_Set() { + // Arrange & Act + PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X) + .AddValue("Y", static t => t.Y) + .Build(); + + // Assert + Assert.IsFalse(formatter.HasBaselines); + } + + [TestMethod] + public void HasBaselines_Is_True_When_Any_Baseline_Is_Set() { + // Arrange & Act + PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X) + .AddValue("Y", static t => t.Y).SetBaseline(100L) + .Build(); + + // Assert + Assert.IsTrue(formatter.HasBaselines); + } + + [TestMethod] + public void HasBaselines_Is_True_When_All_Baselines_Are_Set() { + // Arrange & Act + PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X).SetBaseline(10L) + .AddValue("Y", static t => t.Y).SetBaseline(20L) + .Build(); + + // Assert + Assert.IsTrue(formatter.HasBaselines); + } + + // --------------------------------------------------------------------------- + // PolylineFormatter.GetBaseline + // --------------------------------------------------------------------------- + + [TestMethod] + public void GetBaseline_Returns_Zero_When_No_Baseline_Configured() { + // Arrange + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .Build(); + + // Act + long result = formatter.GetBaseline(0); + + // Assert + Assert.AreEqual(0L, result); + } + + [TestMethod] + public void GetBaseline_Returns_Configured_Baseline() { + // Arrange + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .SetBaseline(42L) + .Build(); + + // Act + long result = formatter.GetBaseline(0); + + // Assert + Assert.AreEqual(42L, result); + } + + [TestMethod] + public void GetBaseline_Returns_Negative_Baseline() { + // Arrange + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .SetBaseline(-1000L) + .Build(); + + // Act + long result = formatter.GetBaseline(0); + + // Assert + Assert.AreEqual(-1000L, result); + } + + // --------------------------------------------------------------------------- + // PolylineFormatter.GetValues + // --------------------------------------------------------------------------- + + [TestMethod] + public void GetValues_Scales_Single_Column_By_Factor() { + // Arrange — precision 5 → factor = 100000; use a value exact in double arithmetic + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v, precision: 5) + .Build(); + + Span output = stackalloc long[1]; + + // Act — 38.5 * 100000 = 3850000 (exactly representable) + formatter.GetValues(38.5, output); + + // Assert + Assert.AreEqual(3850000L, output[0]); + } + + [TestMethod] + public void GetValues_Scales_Multiple_Columns_Independently() { + // Arrange + PolylineFormatter<(double Lat, double Lon)> formatter = + FormatterBuilder<(double Lat, double Lon)>.Create() + .AddValue("Lat", static t => t.Lat, precision: 5) + .AddValue("Lon", static t => t.Lon, precision: 5) + .Build(); + + Span output = stackalloc long[2]; + + // Act — 38.5 * 100000 = 3850000; -120.25 * 100000 = -12025000 (both exact in double) + formatter.GetValues((38.5, -120.25), output); + + // Assert + Assert.AreEqual(3850000L, output[0]); + Assert.AreEqual(-12025000L, output[1]); + } + + [TestMethod] + public void GetValues_With_Zero_Returns_Zero() { + // Arrange + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v, precision: 5) + .Build(); + + Span output = stackalloc long[1]; + + // Act + formatter.GetValues(0.0, output); + + // Assert + Assert.AreEqual(0L, output[0]); + } + + [TestMethod] + public void GetValues_With_Negative_Value_Returns_Negative_Scaled_Long() { + // Arrange + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v, precision: 5) + .Build(); + + Span output = stackalloc long[1]; + + // Act + formatter.GetValues(-90.0, output); + + // Assert + Assert.AreEqual(-9000000L, output[0]); + } + + [TestMethod] + public void GetValues_With_Custom_Precision_Scales_Correctly() { + // Arrange — precision 3 → factor = 1000 + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v, precision: 3) + .Build(); + + Span output = stackalloc long[1]; + + // Act + formatter.GetValues(1.5, output); + + // Assert — 1.5 * 1000 = 1500 + Assert.AreEqual(1500L, output[0]); + } + + // --------------------------------------------------------------------------- + // PolylineOptions constructor validation + // --------------------------------------------------------------------------- + + [TestMethod] + public void PolylineOptions_With_Null_Formatter_Throws_ArgumentNullException() { + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => _ = new PolylineOptions(null!)); + Assert.AreEqual("formatter", ex.ParamName); + } + + // --------------------------------------------------------------------------- + // PolylineOptions properties + // --------------------------------------------------------------------------- + + [TestMethod] + public void PolylineOptions_Stores_Formatter() { + // Arrange + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .Build(); + + // Act + PolylineOptions options = new(formatter); + + // Assert + Assert.AreSame(formatter, options.Formatter); + } + + [TestMethod] + public void PolylineOptions_With_Null_Encoding_Uses_Default_Options() { + // Arrange + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .Build(); + + // Act + PolylineOptions options = new(formatter, null); + + // Assert + Assert.IsNotNull(options.Encoding); + Assert.AreEqual(5u, options.Encoding.Precision); + Assert.AreEqual(512, options.Encoding.StackAllocLimit); + } + + [TestMethod] + public void PolylineOptions_Stores_Custom_Encoding_Options() { + // Arrange + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .Build(); + PolylineEncodingOptions encoding = PolylineEncodingOptionsBuilder.Create() + .WithPrecision(7) + .WithStackAllocLimit(1024) + .Build(); + + // Act + PolylineOptions options = new(formatter, encoding); + + // Assert + Assert.AreSame(encoding, options.Encoding); + Assert.AreEqual(7u, options.Encoding.Precision); + Assert.AreEqual(1024, options.Encoding.StackAllocLimit); + } +} From 2ebebaf79436221c26f79c1181333558af118b6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:36:03 +0000 Subject: [PATCH 02/22] fix: add GetValues buffer-length validation and bounds documentation Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/16db7628-2cc2-4c51-982b-0264a04d7157 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- src/PolylineAlgorithm/PolylineFormatter.cs | 14 ++++++++- .../PolylineFormatterTests.cs | 31 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/PolylineAlgorithm/PolylineFormatter.cs b/src/PolylineAlgorithm/PolylineFormatter.cs index b290115c..3a073dfd 100644 --- a/src/PolylineAlgorithm/PolylineFormatter.cs +++ b/src/PolylineAlgorithm/PolylineFormatter.cs @@ -60,8 +60,17 @@ internal PolylineFormatter(FormatterRule[] rules) { /// Output buffer that receives the scaled values. /// Its length must equal . /// + /// + /// Thrown when .Length does not equal . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void GetValues(T item, Span values) { + if (values.Length != Width) { + throw new ArgumentException( + $"Buffer length {values.Length} does not match the formatter width {Width}.", + nameof(values)); + } + var rules = _rules; // local copy avoids repeated bounds check on the field for (var i = 0; i < rules.Length; i++) { ref var rule = ref rules[i]; @@ -73,7 +82,10 @@ public void GetValues(T item, Span values) { /// Returns the baseline for the column at , or 0 if none is configured. /// The encoder subtracts this value from the first item's scaled column value during encoding. /// - /// The zero-based column index. Must be in the range [0, Width). + /// + /// The zero-based column index. Must be in the range [0, ). + /// An is thrown if the index is out of range. + /// /// The baseline value, or 0 when no baseline has been defined for the column. [MethodImpl(MethodImplOptions.AggressiveInlining)] public long GetBaseline(int index) => _rules[index].Baseline ?? 0L; diff --git a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs index 5102e23f..3d2fdacb 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs @@ -353,6 +353,37 @@ public void GetValues_Scales_Multiple_Columns_Independently() { Assert.AreEqual(-12025000L, output[1]); } + [TestMethod] + public void GetValues_With_Wrong_Buffer_Length_Throws_ArgumentException() { + // Arrange — formatter has Width = 2 but buffer has length 1 + PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + .AddValue("X", static t => t.X) + .AddValue("Y", static t => t.Y) + .Build(); + + long[] tooShort = new long[1]; + + // Act & Assert + ArgumentException ex = Assert.ThrowsExactly( + () => formatter.GetValues((1.0, 2.0), tooShort.AsSpan())); + Assert.AreEqual("values", ex.ParamName); + } + + [TestMethod] + public void GetValues_With_Oversized_Buffer_Throws_ArgumentException() { + // Arrange — formatter has Width = 1 but buffer has length 3 + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .Build(); + + long[] tooLong = new long[3]; + + // Act & Assert + ArgumentException ex = Assert.ThrowsExactly( + () => formatter.GetValues(1.0, tooLong.AsSpan())); + Assert.AreEqual("values", ex.ParamName); + } + [TestMethod] public void GetValues_With_Zero_Returns_Zero() { // Arrange From 7ea10cf32682addb53d38a02e827ce884171d253 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:23:37 +0000 Subject: [PATCH 03/22] Add formatter interfaces, PolylineValueFormatter, PolylineFormatter static class, update base classes and options Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/66b5b819-3735-47b2-a2ec-372ae483e46b Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../Abstraction/AbstractPolylineDecoder.cs | 132 ++++++++++++--- .../Abstraction/AbstractPolylineEncoder.cs | 157 +++++++++++++++--- .../Abstraction/IPolylineFormatter.cs | 42 +++++ .../Abstraction/IPolylineValueFormatter.cs | 55 ++++++ src/PolylineAlgorithm/FormatterBuilder.cs | 39 ++++- .../Internal/DelegatePolylineFormatter.cs | 30 ++++ src/PolylineAlgorithm/PolylineFormatter.cs | 99 +++++------ src/PolylineAlgorithm/PolylineItemFactory.cs | 20 +++ src/PolylineAlgorithm/PolylineOptions.cs | 54 ++++-- .../PolylineValueFormatter.cs | 130 +++++++++++++++ src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 38 +++-- 11 files changed, 662 insertions(+), 134 deletions(-) create mode 100644 src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs create mode 100644 src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs create mode 100644 src/PolylineAlgorithm/Internal/DelegatePolylineFormatter.cs create mode 100644 src/PolylineAlgorithm/PolylineItemFactory.cs create mode 100644 src/PolylineAlgorithm/PolylineValueFormatter.cs diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs index 1059abb9..7971957a 100644 --- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs +++ b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs @@ -14,13 +14,24 @@ namespace PolylineAlgorithm.Abstraction; /// Provides a base implementation for decoding encoded polyline strings into sequences of geographic coordinates. /// /// -/// Derive from this class to implement a decoder for a specific polyline type. Override -/// and to provide type-specific behavior. +/// +/// Formatter-based use (no subclassing required): +/// Supply a via the +/// +/// constructor. The formatters handle all type-specific concerns; override nothing. +/// +/// +/// Legacy override-based use: +/// Derive from this class and override and +/// to provide type-specific behaviour. These overrides take priority over any registered formatter. +/// /// /// The type that represents the encoded polyline input. /// The type that represents a decoded geographic coordinate. -public abstract class AbstractPolylineDecoder : IPolylineDecoder { +public class AbstractPolylineDecoder : IPolylineDecoder { private readonly ILogger> _logger; + private readonly IPolylineValueFormatter? _valueFormatter; + private readonly IPolylineFormatter? _polylineFormatter; /// /// Initializes a new instance of the class with default encoding options. @@ -48,6 +59,35 @@ protected AbstractPolylineDecoder(PolylineEncodingOptions options) { .CreateLogger>(); } + /// + /// Initializes a new instance of the class + /// using the supplied . + /// + /// + /// Use this constructor when you want formatter-driven decoding without subclassing. + /// The and hooks are not called; + /// all type-specific logic is delegated to the formatters. + /// + /// + /// A that carries both the value formatter and + /// the polyline formatter together with the underlying . + /// + /// + /// Thrown when is . + /// + public AbstractPolylineDecoder(PolylineOptions options) { + if (options is null) { + ExceptionGuard.ThrowArgumentNull(nameof(options)); + } + + Options = options.Encoding; + _polylineFormatter = options.PolylineFormatter; + _valueFormatter = options.ValueFormatter; + _logger = Options + .LoggerFactory + .CreateLogger>(); + } + /// /// Gets the encoding options used by this polyline decoder. /// @@ -78,6 +118,7 @@ protected AbstractPolylineDecoder(PolylineEncodingOptions options) { /// /// Thrown when is canceled during decoding. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains two path implementations.")] public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default) { const string OperationName = nameof(Decode); @@ -85,36 +126,67 @@ public IEnumerable Decode(TPolyline polyline, CancellationToken can ValidateNullPolyline(polyline, _logger); - ReadOnlyMemory sequence = GetReadOnlyMemory(in polyline); + ReadOnlyMemory sequence = (_valueFormatter is not null && _polylineFormatter is not null) + ? _polylineFormatter.Read(polyline) + : GetReadOnlyMemory(in polyline); ValidateSequence(sequence, _logger); ValidateFormat(sequence, _logger); int position = 0; - int encodedLatitude = 0; - int encodedLongitude = 0; - try { - while (position < sequence.Length) { - cancellationToken.ThrowIfCancellationRequested(); + if (_valueFormatter is not null && _polylineFormatter is not null) { + int width = _valueFormatter.Width; + int[] accumulated = new int[width]; + long[] longValues = new long[width]; + + try { + while (position < sequence.Length) { + cancellationToken.ThrowIfCancellationRequested(); - if (!PolylineEncoding.TryReadValue(ref encodedLatitude, sequence, ref position) - || !PolylineEncoding.TryReadValue(ref encodedLongitude, sequence, ref position)) { - _logger?.LogOperationFailedDebug(OperationName); - _logger?.LogInvalidPolylineWarning(position); + for (int j = 0; j < width; j++) { + if (!PolylineEncoding.TryReadValue(ref accumulated[j], sequence, ref position)) { + _logger?.LogOperationFailedDebug(OperationName); + _logger?.LogInvalidPolylineWarning(position); + ExceptionGuard.ThrowInvalidPolylineFormat(position); + } + } - ExceptionGuard.ThrowInvalidPolylineFormat(position); + for (int j = 0; j < width; j++) { + longValues[j] = accumulated[j]; + } + + yield return _valueFormatter.CreateItem(longValues.AsSpan()); } + } finally { + _logger?.LogOperationFinishedDebug(OperationName); + } + } else { + int encodedLatitude = 0; + int encodedLongitude = 0; + + try { + while (position < sequence.Length) { + cancellationToken.ThrowIfCancellationRequested(); - double decodedLatitude = PolylineEncoding.Denormalize(encodedLatitude, Options.Precision); - double decodedLongitude = PolylineEncoding.Denormalize(encodedLongitude, Options.Precision); + if (!PolylineEncoding.TryReadValue(ref encodedLatitude, sequence, ref position) + || !PolylineEncoding.TryReadValue(ref encodedLongitude, sequence, ref position)) { + _logger?.LogOperationFailedDebug(OperationName); + _logger?.LogInvalidPolylineWarning(position); - _logger?.LogDecodedCoordinateDebug(decodedLatitude, decodedLongitude, position); + ExceptionGuard.ThrowInvalidPolylineFormat(position); + } - yield return CreateCoordinate(decodedLatitude, decodedLongitude); + double decodedLatitude = PolylineEncoding.Denormalize(encodedLatitude, Options.Precision); + double decodedLongitude = PolylineEncoding.Denormalize(encodedLongitude, Options.Precision); + + _logger?.LogDecodedCoordinateDebug(decodedLatitude, decodedLongitude, position); + + yield return CreateCoordinate(decodedLatitude, decodedLongitude); + } + } finally { + _logger?.LogOperationFinishedDebug(OperationName); } - } finally { - _logger?.LogOperationFinishedDebug(OperationName); } } @@ -184,8 +256,16 @@ protected virtual void ValidateFormat(ReadOnlyMemory sequence, ILogger? lo /// /// A of representing the encoded polyline characters. /// + /// + /// Thrown by the default implementation when no polyline formatter is registered and the method + /// has not been overridden in a derived class. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected abstract ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline); + protected virtual ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline) { + throw new NotSupportedException( + $"Override {nameof(GetReadOnlyMemory)} in a derived class, or provide a " + + $"{nameof(PolylineOptions)} with a polyline formatter."); + } /// /// Creates a instance from the specified latitude and longitude values. @@ -199,6 +279,14 @@ protected virtual void ValidateFormat(ReadOnlyMemory sequence, ILogger? lo /// /// A instance representing the specified geographic coordinate. /// + /// + /// Thrown by the default implementation when no value formatter is registered and the method + /// has not been overridden in a derived class. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected abstract TCoordinate CreateCoordinate(double latitude, double longitude); + protected virtual TCoordinate CreateCoordinate(double latitude, double longitude) { + throw new NotSupportedException( + $"Override {nameof(CreateCoordinate)} in a derived class, or provide a " + + $"{nameof(PolylineOptions)} with a value formatter."); + } } diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs index 1bcdb0ee..3d6a169f 100644 --- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs +++ b/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs @@ -19,13 +19,25 @@ namespace PolylineAlgorithm.Abstraction; /// Provides a base implementation for encoding sequences of geographic coordinates into encoded polyline strings. /// /// -/// Derive from this class to implement an encoder for a specific coordinate and polyline type. Override -/// , , and to provide type-specific behavior. +/// +/// Formatter-based use (no subclassing required): +/// Supply a via the +/// +/// constructor. The formatter handles all type-specific concerns; override nothing. +/// +/// +/// Legacy override-based use: +/// Derive from this class and override , , +/// and to provide type-specific behaviour. These overrides take +/// priority over any registered formatter. +/// /// /// The type that represents a geographic coordinate to encode. /// The type that represents the encoded polyline output. -public abstract class AbstractPolylineEncoder : IPolylineEncoder { +public class AbstractPolylineEncoder : IPolylineEncoder { private readonly ILogger> _logger; + private readonly IPolylineValueFormatter? _valueFormatter; + private readonly IPolylineFormatter? _polylineFormatter; /// /// Initializes a new instance of the class with default encoding options. @@ -51,6 +63,35 @@ protected AbstractPolylineEncoder(PolylineEncodingOptions options) { .CreateLogger>(); } + /// + /// Initializes a new instance of the class + /// using the supplied . + /// + /// + /// Use this constructor when you want formatter-driven encoding without subclassing. + /// The , , and hooks + /// are not called; all type-specific logic is delegated to the formatters. + /// + /// + /// A that carries both the value formatter and + /// the polyline formatter together with the underlying . + /// + /// + /// Thrown when is . + /// + public AbstractPolylineEncoder(PolylineOptions options) { + if (options is null) { + ExceptionGuard.ThrowArgumentNull(nameof(options)); + } + + Options = options.Encoding; + _valueFormatter = options.ValueFormatter; + _polylineFormatter = options.PolylineFormatter; + _logger = Options + .LoggerFactory + .CreateLogger>(); + } + /// /// Gets the encoding options used by this polyline encoder. /// @@ -88,11 +129,15 @@ public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken ValidateEmptyCoordinates(ref coordinates, _logger); + if (_valueFormatter is not null && _polylineFormatter is not null) { + return EncodeWithFormatter(coordinates, cancellationToken); + } + CoordinateDelta delta = new(); int position = 0; int consumed = 0; - int length = GetMaxBufferLength(coordinates.Length); + int length = GetMaxBufferLength(coordinates.Length, 2); char[]? temp = length <= Options.StackAllocLimit ? null @@ -140,17 +185,6 @@ public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken return CreatePolyline(encodedResult.AsMemory()); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static int GetMaxBufferLength(int count) { - Debug.Assert(count > 0, "Count must be greater than zero."); - - int requestedBufferLength = count * 2 * Defaults.Polyline.Block.Length.Max; - - Debug.Assert(requestedBufferLength > 0, "Requested buffer length must be greater than zero."); - - return requestedBufferLength; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] static void ValidateEmptyCoordinates(ref ReadOnlySpan coordinates, ILogger logger) { if (coordinates.Length < 1) { @@ -164,6 +198,56 @@ static void ValidateEmptyCoordinates(ref ReadOnlySpan coordinates, } } + /// + /// Encodes coordinates using the registered value and polyline formatters. + /// + private TPolyline EncodeWithFormatter(ReadOnlySpan coordinates, CancellationToken cancellationToken) { + const string OperationName = nameof(Encode); + int width = _valueFormatter!.Width; + int length = GetMaxBufferLength(coordinates.Length, width); + + char[]? temp = length <= Options.StackAllocLimit + ? null + : ArrayPool.Shared.Rent(length); + + Span buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length); + + int position = 0; + int[] previous = new int[width]; + long[] values = new long[width]; + string encodedResult; + + try { + for (var i = 0; i < coordinates.Length; i++) { + cancellationToken.ThrowIfCancellationRequested(); + + _valueFormatter.GetValues(coordinates[i], values.AsSpan()); + + for (int j = 0; j < width; j++) { + int current = (int)values[j]; + int delta = current - previous[j]; + previous[j] = current; + + if (!PolylineEncoding.TryWriteValue(delta, buffer, ref position)) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogCannotWriteValueToBufferWarning(position, i); + ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer(); + } + } + } + + encodedResult = buffer[..position].ToString(); + } finally { + if (temp is not null) { + ArrayPool.Shared.Return(temp); + } + } + + _logger.LogOperationFinishedDebug(OperationName); + + return _polylineFormatter!.Write(encodedResult.AsMemory()); + } + /// /// Creates a polyline instance from the provided read-only sequence of characters. /// @@ -171,8 +255,16 @@ static void ValidateEmptyCoordinates(ref ReadOnlySpan coordinates, /// /// An instance of representing the encoded polyline. /// + /// + /// Thrown by the default implementation when no polyline formatter is registered and the method + /// has not been overridden in a derived class. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected abstract TPolyline CreatePolyline(ReadOnlyMemory polyline); + protected virtual TPolyline CreatePolyline(ReadOnlyMemory polyline) { + throw new NotSupportedException( + $"Override {nameof(CreatePolyline)} in a derived class, or provide a " + + $"{nameof(PolylineOptions)} with a polyline formatter."); + } /// /// Extracts the longitude value from the specified coordinate. @@ -181,8 +273,16 @@ static void ValidateEmptyCoordinates(ref ReadOnlySpan coordinates, /// /// The longitude value as a . /// + /// + /// Thrown by the default implementation when no value formatter is registered and the method + /// has not been overridden in a derived class. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected abstract double GetLongitude(TCoordinate current); + protected virtual double GetLongitude(TCoordinate current) { + throw new NotSupportedException( + $"Override {nameof(GetLatitude)} and {nameof(GetLongitude)} in a derived class, or " + + $"provide a {nameof(PolylineOptions)} with a value formatter."); + } /// /// Extracts the latitude value from the specified coordinate. @@ -191,7 +291,26 @@ static void ValidateEmptyCoordinates(ref ReadOnlySpan coordinates, /// /// The latitude value as a . /// + /// + /// Thrown by the default implementation when no value formatter is registered and the method + /// has not been overridden in a derived class. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected abstract double GetLatitude(TCoordinate current); -} + protected virtual double GetLatitude(TCoordinate current) { + throw new NotSupportedException( + $"Override {nameof(GetLatitude)} and {nameof(GetLongitude)} in a derived class, or " + + $"provide a {nameof(PolylineOptions)} with a value formatter."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetMaxBufferLength(int count, int valuesPerItem) { + Debug.Assert(count > 0, "Count must be greater than zero."); + Debug.Assert(valuesPerItem > 0, "Values per item must be greater than zero."); + int requestedBufferLength = count * valuesPerItem * Defaults.Polyline.Block.Length.Max; + + Debug.Assert(requestedBufferLength > 0, "Requested buffer length must be greater than zero."); + + return requestedBufferLength; + } +} diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs new file mode 100644 index 00000000..c7ba59e4 --- /dev/null +++ b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs @@ -0,0 +1,42 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm.Abstraction; + +using System; + +/// +/// Defines how to produce a from an encoded character buffer (output/write +/// direction), and how to extract that buffer back from a (input/read +/// direction). +/// +/// The polyline surface type — for example or +/// of . +/// +/// +/// This interface is the polyline-surface counterpart to . +/// The engine exclusively works with of internally. +/// The formatter is the only code that touches . +/// +/// +/// Use , , or +/// to obtain a ready-made implementation. +/// +/// +public interface IPolylineFormatter { + /// + /// Creates a from the encoded character buffer produced by the encoder. + /// + /// The encoded polyline as a read-only span of characters. + /// A wrapping or derived from . + TPolyline Write(ReadOnlyMemory encoded); + + /// + /// Extracts the character buffer from a for the decoder to read. + /// + /// The polyline to read from. + /// A of representing the encoded characters. + ReadOnlyMemory Read(TPolyline polyline); +} diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs b/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs new file mode 100644 index 00000000..01b28ba2 --- /dev/null +++ b/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs @@ -0,0 +1,55 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm.Abstraction; + +using System; + +/// +/// Defines how to extract scaled numeric values from a during encoding, and +/// how to reconstruct a from those values during decoding. +/// +/// The coordinate or item type that the formatter understands. +/// +/// +/// This interface is the coordinate-side counterpart to . +/// Together they allow the engine base classes to be used directly — without subclassing — by supplying +/// both formatters via . +/// +/// +/// Use to build a that +/// already implements this interface. +/// +/// +public interface IPolylineValueFormatter { + /// + /// Extracts and scales all column values from into the span. + /// Called once per item in the encoding loop. + /// + /// The source item from which column values are extracted. + /// + /// Output buffer that receives the scaled integer values. Its length must equal the number of columns + /// defined by this formatter. + /// + void GetValues(TValue item, Span values); + + /// + /// Reconstructs a from the given scaled integer values. + /// Called once per decoded item in the decoding loop. + /// + /// + /// The accumulated scaled integer values decoded from the polyline. Each element corresponds to + /// the same column position as in . + /// + /// A reconstructed from . + TValue CreateItem(ReadOnlySpan values); + + /// + /// Gets the number of values (columns) per encoded item. + /// This is the required length of the buffer passed to and + /// the length of the span received in . + /// + int Width { get; } +} diff --git a/src/PolylineAlgorithm/FormatterBuilder.cs b/src/PolylineAlgorithm/FormatterBuilder.cs index 26021eaf..9a491c02 100644 --- a/src/PolylineAlgorithm/FormatterBuilder.cs +++ b/src/PolylineAlgorithm/FormatterBuilder.cs @@ -10,23 +10,25 @@ namespace PolylineAlgorithm; using System.Collections.Generic; /// -/// Provides a fluent builder for constructing a . +/// Provides a fluent builder for constructing a . /// /// The source object type from which column values are extracted. /// /// /// Use to obtain an instance, call once per column, /// optionally chain to specify an epoch for the most-recently added column, -/// then call to produce the immutable . +/// optionally chain to register a factory for the decoding direction, +/// then call to produce the immutable . /// /// -/// The builder is the only way to create a — its +/// The builder is the only way to create a — its /// constructor is internal. /// /// public sealed class FormatterBuilder { private readonly List> _rules = []; private readonly HashSet _names = new(StringComparer.Ordinal); + private PolylineItemFactory? _create; private FormatterBuilder() { } @@ -101,19 +103,42 @@ public FormatterBuilder SetBaseline(long baseline) { } /// - /// Bakes all added rules into a sealed, immutable . + /// Registers a factory delegate used to reconstruct a from scaled values + /// during decoding. This enables the decoding direction of . + /// + /// + /// A delegate that accepts the scaled integer values decoded from the polyline and returns a + /// . The span length always equals the number of columns added via + /// . + /// + /// The current instance for method chaining. + /// + /// Thrown when is . + /// + public FormatterBuilder WithCreate(PolylineItemFactory create) { + if (create is null) { + throw new ArgumentNullException(nameof(create)); + } + + _create = create; + + return this; + } + + /// + /// Bakes all added rules into a sealed, immutable . /// /// - /// An immutable whose rules can no longer be changed. + /// An immutable whose rules can no longer be changed. /// /// /// Thrown when no rules have been added. /// - public PolylineFormatter Build() { + public PolylineValueFormatter Build() { if (_rules.Count == 0) { throw new InvalidOperationException("At least one rule must be added before calling Build."); } - return new PolylineFormatter(_rules.ToArray()); + return new PolylineValueFormatter(_rules.ToArray(), _create); } } diff --git a/src/PolylineAlgorithm/Internal/DelegatePolylineFormatter.cs b/src/PolylineAlgorithm/Internal/DelegatePolylineFormatter.cs new file mode 100644 index 00000000..82d44b39 --- /dev/null +++ b/src/PolylineAlgorithm/Internal/DelegatePolylineFormatter.cs @@ -0,0 +1,30 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm.Internal; + +using PolylineAlgorithm.Abstraction; +using System; + +/// +/// A sealed delegate-backed implementation of . +/// Produced by and the built-in factory properties. +/// +/// The polyline surface type. +internal sealed class DelegatePolylineFormatter : IPolylineFormatter { + private readonly Func, T> _write; + private readonly Func> _read; + + internal DelegatePolylineFormatter(Func, T> write, Func> read) { + _write = write; + _read = read; + } + + /// + public T Write(ReadOnlyMemory encoded) => _write(encoded); + + /// + public ReadOnlyMemory Read(T polyline) => _read(polyline); +} diff --git a/src/PolylineAlgorithm/PolylineFormatter.cs b/src/PolylineAlgorithm/PolylineFormatter.cs index 3a073dfd..d2be5198 100644 --- a/src/PolylineAlgorithm/PolylineFormatter.cs +++ b/src/PolylineAlgorithm/PolylineFormatter.cs @@ -5,88 +5,65 @@ namespace PolylineAlgorithm; +using PolylineAlgorithm.Abstraction; using PolylineAlgorithm.Internal; using System; -using System.Runtime.CompilerServices; /// -/// Provides an immutable, sealed rule engine that describes how to extract and scale values from -/// an object of type for polyline encoding. +/// Provides static factory methods and ready-made instances of +/// for the most common polyline surface types. /// -/// The source object type from which column values are extracted. /// /// -/// Instances of this class are constructed exclusively through . -/// -/// -/// The modifier allows the JIT to devirtualise and inline calls to -/// , eliminating vtable dispatch in the encoding hot loop. +/// Use or for the two most common cases. +/// Call to build a custom formatter from a pair of delegates. /// /// -public sealed class PolylineFormatter { - private readonly FormatterRule[] _rules; - - /// - /// Initializes a new instance of with the baked rules. - /// This constructor is intentionally internal; use to create instances. - /// - /// The pre-calculated rules array produced by the builder. - internal PolylineFormatter(FormatterRule[] rules) { - _rules = rules; - Width = rules.Length; - HasBaselines = Array.Exists(rules, static r => r.Baseline.HasValue); - } - +public static class PolylineFormatter { /// - /// Gets the number of columns (values per item). - /// This is the required length of the passed to . + /// Gets a formatter that produces a from the encoded char buffer and reads + /// the buffer back via . /// - public int Width { get; } + public static IPolylineFormatter ForString { get; } = + new DelegatePolylineFormatter( + static mem => new string(mem.Span), + static s => s.AsMemory()); /// - /// Gets a value indicating whether any column has a baseline defined. - /// When the encoder can skip the baseline-subtraction branch entirely, - /// keeping the common-case encoding path branch-free. + /// Gets a pass-through formatter for of . + /// Both Write and Read are identity operations. /// - public bool HasBaselines { get; } + public static IPolylineFormatter> ForMemory { get; } = + new DelegatePolylineFormatter>( + static mem => mem, + static mem => mem); /// - /// Extracts and scales all column values from into the span. - /// Called once per item in the encoding hot loop. This method performs no heap allocation; - /// the caller is responsible for providing and owning the output buffer. + /// Creates a custom from a pair of delegates. /// - /// The source item from which column values are extracted. - /// - /// Output buffer that receives the scaled values. - /// Its length must equal . + /// The polyline surface type. + /// + /// Converts the encoded of produced by the encoder + /// into a . /// - /// - /// Thrown when .Length does not equal . + /// + /// Extracts the encoded character buffer from a for the decoder to consume. + /// + /// A sealed backed by the supplied delegates. + /// + /// Thrown when or is . /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void GetValues(T item, Span values) { - if (values.Length != Width) { - throw new ArgumentException( - $"Buffer length {values.Length} does not match the formatter width {Width}.", - nameof(values)); + public static IPolylineFormatter Create( + Func, T> write, + Func> read) { + if (write is null) { + throw new ArgumentNullException(nameof(write)); } - var rules = _rules; // local copy avoids repeated bounds check on the field - for (var i = 0; i < rules.Length; i++) { - ref var rule = ref rules[i]; - values[i] = (long)(rule.Select(item) * rule.Factor); + if (read is null) { + throw new ArgumentNullException(nameof(read)); } - } - /// - /// Returns the baseline for the column at , or 0 if none is configured. - /// The encoder subtracts this value from the first item's scaled column value during encoding. - /// - /// - /// The zero-based column index. Must be in the range [0, ). - /// An is thrown if the index is out of range. - /// - /// The baseline value, or 0 when no baseline has been defined for the column. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long GetBaseline(int index) => _rules[index].Baseline ?? 0L; + return new DelegatePolylineFormatter(write, read); + } } diff --git a/src/PolylineAlgorithm/PolylineItemFactory.cs b/src/PolylineAlgorithm/PolylineItemFactory.cs new file mode 100644 index 00000000..f977133e --- /dev/null +++ b/src/PolylineAlgorithm/PolylineItemFactory.cs @@ -0,0 +1,20 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +using System; + +/// +/// Represents a factory method that reconstructs a item from an array of +/// scaled integer values decoded from a polyline. +/// +/// The coordinate or item type to create. +/// +/// The scaled integer values accumulated from the polyline decoder. Each element corresponds to one +/// column as defined by the that built the associated formatter. +/// +/// A instance reconstructed from . +public delegate T PolylineItemFactory(ReadOnlySpan values); diff --git a/src/PolylineAlgorithm/PolylineOptions.cs b/src/PolylineAlgorithm/PolylineOptions.cs index f5c38ff7..d0189797 100644 --- a/src/PolylineAlgorithm/PolylineOptions.cs +++ b/src/PolylineAlgorithm/PolylineOptions.cs @@ -5,43 +5,67 @@ namespace PolylineAlgorithm; +using PolylineAlgorithm.Abstraction; using System; /// -/// Provides configuration for a -driven encoding operation. +/// Provides unified configuration for a formatter-driven encoding or decoding operation. /// -/// The source object type from which column values are extracted. +/// The coordinate or item type understood by the value formatter. +/// The polyline surface type understood by the polyline formatter. /// -/// Combines a (which defines the column schema and scaling rules) -/// with a (which controls buffer sizes, precision, and logging). +/// Combines an (which defines the column schema, scaling +/// rules, and item factory) with an (which converts between +/// the raw character buffer and the surface type) and a (which +/// controls buffer sizes, precision for legacy paths, and logging). /// -public sealed class PolylineOptions { +public sealed class PolylineOptions { /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// - /// - /// The sealed formatter that defines the column schema. Must not be . + /// + /// The formatter that defines the column schema, scaling rules, and item factory. Must not be + /// . + /// + /// + /// The formatter that converts between the raw character buffer and + /// . Must not be . /// /// /// The encoding options that control buffer sizes, precision, and logging. /// Pass to use default options. /// /// - /// Thrown when is . + /// Thrown when or is + /// . /// - public PolylineOptions(PolylineFormatter formatter, PolylineEncodingOptions? encoding = null) { - if (formatter is null) { - throw new ArgumentNullException(nameof(formatter)); + public PolylineOptions( + IPolylineValueFormatter valueFormatter, + IPolylineFormatter polylineFormatter, + PolylineEncodingOptions? encoding = null) { + if (valueFormatter is null) { + throw new ArgumentNullException(nameof(valueFormatter)); + } + + if (polylineFormatter is null) { + throw new ArgumentNullException(nameof(polylineFormatter)); } - Formatter = formatter; + ValueFormatter = valueFormatter; + PolylineFormatter = polylineFormatter; Encoding = encoding ?? new PolylineEncodingOptions(); } /// - /// Gets the sealed formatter that defines the column schema and scaling rules. + /// Gets the formatter that defines the column schema, scaling rules, and item factory. + /// + public IPolylineValueFormatter ValueFormatter { get; } + + /// + /// Gets the formatter that converts between the raw character buffer and + /// . /// - public PolylineFormatter Formatter { get; } + public IPolylineFormatter PolylineFormatter { get; } /// /// Gets the encoding options that control buffer sizes, precision, and logging. diff --git a/src/PolylineAlgorithm/PolylineValueFormatter.cs b/src/PolylineAlgorithm/PolylineValueFormatter.cs new file mode 100644 index 00000000..3c24701c --- /dev/null +++ b/src/PolylineAlgorithm/PolylineValueFormatter.cs @@ -0,0 +1,130 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +using PolylineAlgorithm.Abstraction; +using PolylineAlgorithm.Internal; +using System; +using System.Runtime.CompilerServices; + +/// +/// Provides an immutable, sealed rule engine that describes how to extract and scale values from +/// an object of type for polyline encoding, and how to reconstruct an +/// object of type from scaled values during decoding. +/// +/// The source object type from which column values are extracted or to which they are reconstructed. +/// +/// +/// Instances of this class are constructed exclusively through . +/// +/// +/// The modifier allows the JIT to devirtualise and inline calls to +/// and , eliminating vtable dispatch in the +/// encoding/decoding hot loop. +/// +/// +public sealed class PolylineValueFormatter : IPolylineValueFormatter { + private readonly FormatterRule[] _rules; + private readonly PolylineItemFactory? _create; + + /// + /// Initializes a new instance of with the baked rules. + /// This constructor is intentionally internal; use to create instances. + /// + /// The pre-calculated rules array produced by the builder. + /// + /// An optional factory delegate that reconstructs a from scaled values. + /// Required for the decoding direction; if , throws. + /// + internal PolylineValueFormatter(FormatterRule[] rules, PolylineItemFactory? create = null) { + _rules = rules; + _create = create; + Width = rules.Length; + HasBaselines = Array.Exists(rules, static r => r.Baseline.HasValue); + } + + /// + /// Gets the number of columns (values per item). + /// This is the required length of the passed to + /// and the length of the span received by . + /// + public int Width { get; } + + /// + /// Gets a value indicating whether any column has a baseline defined. + /// When the encoder can skip the baseline-subtraction branch entirely, + /// keeping the common-case encoding path branch-free. + /// + public bool HasBaselines { get; } + + /// + /// Gets a value indicating whether a factory delegate was supplied at build time. + /// When , calling throws an + /// . + /// + public bool CanCreateItem => _create is not null; + + /// + /// Extracts and scales all column values from into the span. + /// Called once per item in the encoding hot loop. This method performs no heap allocation; + /// the caller is responsible for providing and owning the output buffer. + /// + /// The source item from which column values are extracted. + /// + /// Output buffer that receives the scaled values. + /// Its length must equal . + /// + /// + /// Thrown when .Length does not equal . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GetValues(T item, Span values) { + if (values.Length != Width) { + throw new ArgumentException( + $"Buffer length {values.Length} does not match the formatter width {Width}.", + nameof(values)); + } + + var rules = _rules; // local copy avoids repeated bounds check on the field + for (var i = 0; i < rules.Length; i++) { + ref var rule = ref rules[i]; + values[i] = (long)(rule.Select(item) * rule.Factor); + } + } + + /// + /// Reconstructs a from the given scaled integer values. + /// + /// + /// The accumulated scaled integer values decoded from the polyline. + /// + /// A reconstructed from . + /// + /// Thrown when no factory delegate was supplied via . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T CreateItem(ReadOnlySpan values) { + if (_create is null) { + throw new InvalidOperationException( + $"Cannot reconstruct an item because no factory was registered. " + + $"Call {nameof(FormatterBuilder)}.{nameof(FormatterBuilder.WithCreate)} before building."); + } + + return _create(values); + } + + /// + /// Returns the baseline for the column at , or 0 if none is configured. + /// The encoder subtracts this value from the first item's scaled column value during encoding. + /// + /// + /// The zero-based column index. Must be in the range [0, ). + /// An is thrown if the index is out of range. + /// + /// The baseline value, or 0 when no baseline has been defined for the column. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long GetBaseline(int index) => _rules[index].Baseline ?? 0L; +} diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index 32a0147c..8f358994 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -1,15 +1,33 @@ #nullable enable PolylineAlgorithm.FormatterBuilder PolylineAlgorithm.FormatterBuilder.AddValue(string! name, System.Func! selector, uint precision = 5) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.Build() -> PolylineAlgorithm.PolylineFormatter! +PolylineAlgorithm.FormatterBuilder.Build() -> PolylineAlgorithm.PolylineValueFormatter! PolylineAlgorithm.FormatterBuilder.SetBaseline(long baseline) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.PolylineFormatter -PolylineAlgorithm.PolylineFormatter.GetBaseline(int index) -> long -PolylineAlgorithm.PolylineFormatter.GetValues(T item, System.Span values) -> void -PolylineAlgorithm.PolylineFormatter.HasBaselines.get -> bool -PolylineAlgorithm.PolylineFormatter.Width.get -> int -PolylineAlgorithm.PolylineOptions -PolylineAlgorithm.PolylineOptions.Encoding.get -> PolylineAlgorithm.PolylineEncodingOptions! -PolylineAlgorithm.PolylineOptions.Formatter.get -> PolylineAlgorithm.PolylineFormatter! -PolylineAlgorithm.PolylineOptions.PolylineOptions(PolylineAlgorithm.PolylineFormatter! formatter, PolylineAlgorithm.PolylineEncodingOptions? encoding = null) -> void +PolylineAlgorithm.FormatterBuilder.WithCreate(PolylineAlgorithm.PolylineItemFactory! create) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.Abstraction.IPolylineFormatter +PolylineAlgorithm.Abstraction.IPolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory +PolylineAlgorithm.Abstraction.IPolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline +PolylineAlgorithm.Abstraction.IPolylineValueFormatter +PolylineAlgorithm.Abstraction.IPolylineValueFormatter.CreateItem(System.ReadOnlySpan values) -> TValue +PolylineAlgorithm.Abstraction.IPolylineValueFormatter.GetValues(TValue item, System.Span values) -> void +PolylineAlgorithm.Abstraction.IPolylineValueFormatter.Width.get -> int +PolylineAlgorithm.PolylineFormatter +static PolylineAlgorithm.PolylineFormatter.ForMemory.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter>! +static PolylineAlgorithm.PolylineFormatter.ForString.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! +static PolylineAlgorithm.PolylineFormatter.Create(System.Func, T>! write, System.Func>! read) -> PolylineAlgorithm.Abstraction.IPolylineFormatter! +PolylineAlgorithm.PolylineItemFactory +PolylineAlgorithm.PolylineValueFormatter +PolylineAlgorithm.PolylineValueFormatter.CanCreateItem.get -> bool +PolylineAlgorithm.PolylineValueFormatter.CreateItem(System.ReadOnlySpan values) -> T +PolylineAlgorithm.PolylineValueFormatter.GetBaseline(int index) -> long +PolylineAlgorithm.PolylineValueFormatter.GetValues(T item, System.Span values) -> void +PolylineAlgorithm.PolylineValueFormatter.HasBaselines.get -> bool +PolylineAlgorithm.PolylineValueFormatter.Width.get -> int +PolylineAlgorithm.PolylineOptions +PolylineAlgorithm.PolylineOptions.Encoding.get -> PolylineAlgorithm.PolylineEncodingOptions! +PolylineAlgorithm.PolylineOptions.PolylineFormatter.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! +PolylineAlgorithm.PolylineOptions.PolylineOptions(PolylineAlgorithm.Abstraction.IPolylineValueFormatter! valueFormatter, PolylineAlgorithm.Abstraction.IPolylineFormatter! polylineFormatter, PolylineAlgorithm.PolylineEncodingOptions? encoding = null) -> void +PolylineAlgorithm.PolylineOptions.ValueFormatter.get -> PolylineAlgorithm.Abstraction.IPolylineValueFormatter! +PolylineAlgorithm.Abstraction.AbstractPolylineEncoder.AbstractPolylineEncoder(PolylineAlgorithm.PolylineOptions! options) -> void +PolylineAlgorithm.Abstraction.AbstractPolylineDecoder.AbstractPolylineDecoder(PolylineAlgorithm.PolylineOptions! options) -> void static PolylineAlgorithm.FormatterBuilder.Create() -> PolylineAlgorithm.FormatterBuilder! From 3482dac257b368332a470bdbd8c375e059df3955 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:00:26 +0000 Subject: [PATCH 04/22] =?UTF-8?q?Fix=20tests:=20rename=20PolylineFormatter?= =?UTF-8?q?=20=E2=86=92=20PolylineValueFormatter,=20update=20Polylin?= =?UTF-8?q?eOptions=20to=20two-generic=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/d0d5745e-96e3-4147-9b10-d3faf9374824 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../PolylineFormatterTests.cs | 105 +++++++++++------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs index 3d2fdacb..7fbfd1da 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs @@ -9,8 +9,8 @@ namespace PolylineAlgorithm.Tests; using System; /// -/// Tests for , , -/// and . +/// Tests for , , +/// and . /// [TestClass] public sealed class PolylineFormatterTests { @@ -105,7 +105,7 @@ public void AddValue_Returns_Same_Builder_For_Method_Chaining() { [TestMethod] public void AddValue_With_Different_Names_Succeeds() { // Arrange & Act - PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .Build(); @@ -147,7 +147,7 @@ public void SetBaseline_Returns_Same_Builder_For_Method_Chaining() { [TestMethod] public void SetBaseline_Applies_Only_To_Last_Added_Rule() { // Arrange & Act - PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .SetBaseline(500L) @@ -161,7 +161,7 @@ public void SetBaseline_Applies_Only_To_Last_Added_Rule() { [TestMethod] public void SetBaseline_Can_Be_Called_On_Each_Rule() { // Arrange & Act - PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() .AddValue("X", static t => t.X).SetBaseline(100L) .AddValue("Y", static t => t.Y).SetBaseline(200L) .Build(); @@ -191,21 +191,21 @@ public void Build_With_Multiple_Invocations_Returns_Different_Instances() { .AddValue("X", static t => t.X); // Act - PolylineFormatter<(double X, double Y)> first = builder.Build(); - PolylineFormatter<(double X, double Y)> second = builder.Build(); + PolylineValueFormatter<(double X, double Y)> first = builder.Build(); + PolylineValueFormatter<(double X, double Y)> second = builder.Build(); // Assert Assert.AreNotSame(first, second); } // --------------------------------------------------------------------------- - // PolylineFormatter.Width + // PolylineValueFormatter.Width // --------------------------------------------------------------------------- [TestMethod] public void Width_Equals_Number_Of_Added_Rules() { // Arrange & Act - PolylineFormatter<(double X, double Y, double Z)> formatter = FormatterBuilder<(double X, double Y, double Z)>.Create() + PolylineValueFormatter<(double X, double Y, double Z)> formatter = FormatterBuilder<(double X, double Y, double Z)>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .AddValue("Z", static t => t.Z) @@ -218,7 +218,7 @@ public void Width_Equals_Number_Of_Added_Rules() { [TestMethod] public void Width_Is_One_For_Single_Rule() { // Arrange & Act - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .Build(); @@ -227,13 +227,13 @@ public void Width_Is_One_For_Single_Rule() { } // --------------------------------------------------------------------------- - // PolylineFormatter.HasBaselines + // PolylineValueFormatter.HasBaselines // --------------------------------------------------------------------------- [TestMethod] public void HasBaselines_Is_False_When_No_Baselines_Are_Set() { // Arrange & Act - PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .Build(); @@ -245,7 +245,7 @@ public void HasBaselines_Is_False_When_No_Baselines_Are_Set() { [TestMethod] public void HasBaselines_Is_True_When_Any_Baseline_Is_Set() { // Arrange & Act - PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y).SetBaseline(100L) .Build(); @@ -257,7 +257,7 @@ public void HasBaselines_Is_True_When_Any_Baseline_Is_Set() { [TestMethod] public void HasBaselines_Is_True_When_All_Baselines_Are_Set() { // Arrange & Act - PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() .AddValue("X", static t => t.X).SetBaseline(10L) .AddValue("Y", static t => t.Y).SetBaseline(20L) .Build(); @@ -267,13 +267,13 @@ public void HasBaselines_Is_True_When_All_Baselines_Are_Set() { } // --------------------------------------------------------------------------- - // PolylineFormatter.GetBaseline + // PolylineValueFormatter.GetBaseline // --------------------------------------------------------------------------- [TestMethod] public void GetBaseline_Returns_Zero_When_No_Baseline_Configured() { // Arrange - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .Build(); @@ -287,7 +287,7 @@ public void GetBaseline_Returns_Zero_When_No_Baseline_Configured() { [TestMethod] public void GetBaseline_Returns_Configured_Baseline() { // Arrange - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .SetBaseline(42L) .Build(); @@ -302,7 +302,7 @@ public void GetBaseline_Returns_Configured_Baseline() { [TestMethod] public void GetBaseline_Returns_Negative_Baseline() { // Arrange - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .SetBaseline(-1000L) .Build(); @@ -315,13 +315,13 @@ public void GetBaseline_Returns_Negative_Baseline() { } // --------------------------------------------------------------------------- - // PolylineFormatter.GetValues + // PolylineValueFormatter.GetValues // --------------------------------------------------------------------------- [TestMethod] public void GetValues_Scales_Single_Column_By_Factor() { // Arrange — precision 5 → factor = 100000; use a value exact in double arithmetic - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) .Build(); @@ -337,7 +337,7 @@ public void GetValues_Scales_Single_Column_By_Factor() { [TestMethod] public void GetValues_Scales_Multiple_Columns_Independently() { // Arrange - PolylineFormatter<(double Lat, double Lon)> formatter = + PolylineValueFormatter<(double Lat, double Lon)> formatter = FormatterBuilder<(double Lat, double Lon)>.Create() .AddValue("Lat", static t => t.Lat, precision: 5) .AddValue("Lon", static t => t.Lon, precision: 5) @@ -356,7 +356,7 @@ public void GetValues_Scales_Multiple_Columns_Independently() { [TestMethod] public void GetValues_With_Wrong_Buffer_Length_Throws_ArgumentException() { // Arrange — formatter has Width = 2 but buffer has length 1 - PolylineFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .Build(); @@ -372,7 +372,7 @@ public void GetValues_With_Wrong_Buffer_Length_Throws_ArgumentException() { [TestMethod] public void GetValues_With_Oversized_Buffer_Throws_ArgumentException() { // Arrange — formatter has Width = 1 but buffer has length 3 - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .Build(); @@ -387,7 +387,7 @@ public void GetValues_With_Oversized_Buffer_Throws_ArgumentException() { [TestMethod] public void GetValues_With_Zero_Returns_Zero() { // Arrange - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) .Build(); @@ -403,7 +403,7 @@ public void GetValues_With_Zero_Returns_Zero() { [TestMethod] public void GetValues_With_Negative_Value_Returns_Negative_Scaled_Long() { // Arrange - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) .Build(); @@ -419,7 +419,7 @@ public void GetValues_With_Negative_Value_Returns_Negative_Scaled_Long() { [TestMethod] public void GetValues_With_Custom_Precision_Scales_Correctly() { // Arrange — precision 3 → factor = 1000 - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 3) .Build(); @@ -433,44 +433,71 @@ public void GetValues_With_Custom_Precision_Scales_Correctly() { } // --------------------------------------------------------------------------- - // PolylineOptions constructor validation + // PolylineOptions constructor validation // --------------------------------------------------------------------------- [TestMethod] - public void PolylineOptions_With_Null_Formatter_Throws_ArgumentNullException() { + public void PolylineOptions_With_Null_ValueFormatter_Throws_ArgumentNullException() { // Act & Assert ArgumentNullException ex = Assert.ThrowsExactly( - () => _ = new PolylineOptions(null!)); - Assert.AreEqual("formatter", ex.ParamName); + () => _ = new PolylineOptions(null!, PolylineFormatter.ForString)); + Assert.AreEqual("valueFormatter", ex.ParamName); + } + + [TestMethod] + public void PolylineOptions_With_Null_PolylineFormatter_Throws_ArgumentNullException() { + // Arrange + PolylineValueFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .Build(); + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => _ = new PolylineOptions(formatter, null!)); + Assert.AreEqual("polylineFormatter", ex.ParamName); } // --------------------------------------------------------------------------- - // PolylineOptions properties + // PolylineOptions properties // --------------------------------------------------------------------------- [TestMethod] - public void PolylineOptions_Stores_Formatter() { + public void PolylineOptions_Stores_ValueFormatter() { + // Arrange + PolylineValueFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .Build(); + + // Act + PolylineOptions options = new(formatter, PolylineFormatter.ForString); + + // Assert + Assert.AreSame(formatter, options.ValueFormatter); + } + + [TestMethod] + public void PolylineOptions_Stores_PolylineFormatter() { // Arrange - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .Build(); // Act - PolylineOptions options = new(formatter); + PolylineOptions options = new(formatter, PolylineFormatter.ForString); // Assert - Assert.AreSame(formatter, options.Formatter); + Assert.AreSame(PolylineFormatter.ForString, options.PolylineFormatter); } [TestMethod] public void PolylineOptions_With_Null_Encoding_Uses_Default_Options() { // Arrange - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .Build(); // Act - PolylineOptions options = new(formatter, null); + PolylineOptions options = new(formatter, PolylineFormatter.ForString, null); // Assert Assert.IsNotNull(options.Encoding); @@ -481,7 +508,7 @@ public void PolylineOptions_With_Null_Encoding_Uses_Default_Options() { [TestMethod] public void PolylineOptions_Stores_Custom_Encoding_Options() { // Arrange - PolylineFormatter formatter = FormatterBuilder.Create() + PolylineValueFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .Build(); PolylineEncodingOptions encoding = PolylineEncodingOptionsBuilder.Create() @@ -490,7 +517,7 @@ public void PolylineOptions_Stores_Custom_Encoding_Options() { .Build(); // Act - PolylineOptions options = new(formatter, encoding); + PolylineOptions options = new(formatter, PolylineFormatter.ForString, encoding); // Assert Assert.AreSame(encoding, options.Encoding); From 794cbf7ba873850d0dbbd51df2de56c08b57a28f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 02:03:37 +0000 Subject: [PATCH 05/22] =?UTF-8?q?Fix=20baseline=20not=20applied=20in=20Enc?= =?UTF-8?q?odeWithFormatter,=20long=E2=86=92int=20delta=20arithmetic,=20do?= =?UTF-8?q?c=20comment,=20add=20formatter-path=20integration=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/f4994093-4128-4101-b5df-f6a031463712 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../Abstraction/AbstractPolylineDecoder.cs | 4 +- .../Abstraction/AbstractPolylineEncoder.cs | 12 +- .../Abstraction/IPolylineValueFormatter.cs | 8 ++ src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 1 + .../AbstractPolylineDecoderTests.cs | 96 ++++++++++++++ .../AbstractPolylineEncoderTests.cs | 123 ++++++++++++++++++ 6 files changed, 238 insertions(+), 6 deletions(-) diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs index 7971957a..5a637818 100644 --- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs +++ b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs @@ -16,8 +16,8 @@ namespace PolylineAlgorithm.Abstraction; /// /// /// Formatter-based use (no subclassing required): -/// Supply a via the -/// +/// Supply a via the +/// /// constructor. The formatters handle all type-specific concerns; override nothing. /// /// diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs index 3d6a169f..8873d939 100644 --- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs +++ b/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs @@ -213,10 +213,14 @@ private TPolyline EncodeWithFormatter(ReadOnlySpan coordinates, Can Span buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length); int position = 0; - int[] previous = new int[width]; + long[] previous = new long[width]; long[] values = new long[width]; string encodedResult; + for (int j = 0; j < width; j++) { + previous[j] = _valueFormatter.GetBaseline(j); + } + try { for (var i = 0; i < coordinates.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); @@ -224,11 +228,11 @@ private TPolyline EncodeWithFormatter(ReadOnlySpan coordinates, Can _valueFormatter.GetValues(coordinates[i], values.AsSpan()); for (int j = 0; j < width; j++) { - int current = (int)values[j]; - int delta = current - previous[j]; + long current = values[j]; + long delta = current - previous[j]; previous[j] = current; - if (!PolylineEncoding.TryWriteValue(delta, buffer, ref position)) { + if (!PolylineEncoding.TryWriteValue((int)delta, buffer, ref position)) { _logger.LogOperationFailedDebug(OperationName); _logger.LogCannotWriteValueToBufferWarning(position, i); ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer(); diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs b/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs index 01b28ba2..c6c1a204 100644 --- a/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs +++ b/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs @@ -52,4 +52,12 @@ public interface IPolylineValueFormatter { /// the length of the span received in . /// int Width { get; } + + /// + /// Returns the baseline (epoch) for the column at , or 0 if none is configured. + /// The encoder subtracts this value from the first item's scaled column value to keep the initial delta small. + /// + /// The zero-based column index. Must be in the range [0, ). + /// The baseline value, or 0 when no baseline has been defined for the column. + long GetBaseline(int index) => 0L; } diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index 8f358994..c62b223c 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -9,6 +9,7 @@ PolylineAlgorithm.Abstraction.IPolylineFormatter.Read(TPolyline polyl PolylineAlgorithm.Abstraction.IPolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline PolylineAlgorithm.Abstraction.IPolylineValueFormatter PolylineAlgorithm.Abstraction.IPolylineValueFormatter.CreateItem(System.ReadOnlySpan values) -> TValue +PolylineAlgorithm.Abstraction.IPolylineValueFormatter.GetBaseline(int index) -> long PolylineAlgorithm.Abstraction.IPolylineValueFormatter.GetValues(TValue item, System.Span values) -> void PolylineAlgorithm.Abstraction.IPolylineValueFormatter.Width.get -> int PolylineAlgorithm.PolylineFormatter diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs index c6734c6c..7e6a2472 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs @@ -9,6 +9,7 @@ namespace PolylineAlgorithm.Tests.Abstraction; using PolylineAlgorithm.Utility; using System; using System.Collections.Generic; +using PolylineAlgorithm; /// /// Tests for . @@ -141,4 +142,99 @@ public void Decode_With_Pre_Cancelled_Token_Throws_OperationCanceledException() // Act & Assert Assert.ThrowsExactly(() => decoder.Decode(polyline, cts.Token).ToList()); } + + // ------------------------------------------------------------------------- + // Formatter-based path (PolylineOptions constructor) + // ------------------------------------------------------------------------- + + /// + /// Tests that the PolylineOptions constructor with null throws . + /// + [TestMethod] + public void Constructor_With_Null_PolylineOptions_Throws_ArgumentNullException() { + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => new AbstractPolylineDecoder((PolylineOptions<(double, double), string>)null!)); + Assert.AreEqual("options", ex.ParamName); + } + + /// + /// Tests that the formatter-based decoder decodes the known polyline to the expected coordinates. + /// + [TestMethod] + public void Decode_FormatterPath_With_Known_Polyline_Returns_Expected_Coordinates() { + // Arrange + PolylineValueFormatter<(double Latitude, double Longitude)> valueFormatter = + FormatterBuilder<(double Latitude, double Longitude)>.Create() + .AddValue("lat", c => c.Latitude) + .AddValue("lon", c => c.Longitude) + .WithCreate(static values => (values[0] / 1e5, values[1] / 1e5)) + .Build(); + + PolylineOptions<(double Latitude, double Longitude), string> options = new( + valueFormatter, + PolylineFormatter.ForString); + + AbstractPolylineDecoder decoder = new(options); + + string polyline = StaticValueProvider.Valid.GetPolyline(); + (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; + + // Act + (double Latitude, double Longitude)[] result = [.. decoder.Decode(polyline)]; + + // Assert + Assert.AreEqual(expected.Length, result.Length); + for (int i = 0; i < expected.Length; i++) { + Assert.AreEqual(expected[i].Latitude, result[i].Latitude, 1e-5); + Assert.AreEqual(expected[i].Longitude, result[i].Longitude, 1e-5); + } + } + + /// + /// Tests that the formatter-based decoder throws for a null polyline. + /// + [TestMethod] + public void Decode_FormatterPath_With_Null_Polyline_Throws_ArgumentNullException() { + // Arrange + PolylineValueFormatter<(double, double)> valueFormatter = + FormatterBuilder<(double, double)>.Create() + .AddValue("lat", c => c.Item1) + .AddValue("lon", c => c.Item2) + .WithCreate(static values => (values[0] / 1e5, values[1] / 1e5)) + .Build(); + + AbstractPolylineDecoder decoder = new( + new PolylineOptions<(double, double), string>(valueFormatter, PolylineFormatter.ForString)); + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => decoder.Decode(null!).ToList()); + Assert.AreEqual("polyline", ex.ParamName); + } + + /// + /// Tests that the formatter-based decoder with a pre-cancelled token throws . + /// + [TestMethod] + public void Decode_FormatterPath_With_Pre_Cancelled_Token_Throws_OperationCanceledException() { + // Arrange + PolylineValueFormatter<(double, double)> valueFormatter = + FormatterBuilder<(double, double)>.Create() + .AddValue("lat", c => c.Item1) + .AddValue("lon", c => c.Item2) + .WithCreate(static values => (values[0] / 1e5, values[1] / 1e5)) + .Build(); + + AbstractPolylineDecoder decoder = new( + new PolylineOptions<(double, double), string>(valueFormatter, PolylineFormatter.ForString)); + + string polyline = StaticValueProvider.Valid.GetPolyline(); + using CancellationTokenSource cts = new(); + cts.Cancel(); + + // Act & Assert + Assert.ThrowsExactly( + () => decoder.Decode(polyline, cts.Token).ToList()); + } } diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs index f537dda2..0403fa1f 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs @@ -8,6 +8,7 @@ namespace PolylineAlgorithm.Tests.Abstraction; using PolylineAlgorithm.Abstraction; using PolylineAlgorithm.Utility; using System; +using PolylineAlgorithm; /// /// Tests for . @@ -149,4 +150,126 @@ public void Encode_With_Small_Stack_Alloc_Limit_Uses_Heap_Allocation_And_Produce // Assert Assert.AreEqual(expected, result); } + + // ------------------------------------------------------------------------- + // Formatter-based path (PolylineOptions constructor) + // ------------------------------------------------------------------------- + + /// + /// Tests that the PolylineOptions constructor with null throws . + /// + [TestMethod] + public void Constructor_With_Null_PolylineOptions_Throws_ArgumentNullException() { + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => new AbstractPolylineEncoder<(double, double), string>((PolylineOptions<(double, double), string>)null!)); + Assert.AreEqual("options", ex.ParamName); + } + + /// + /// Tests that the formatter-based encoder round-trips known coordinates through the polyline wire format. + /// + [TestMethod] + public void Encode_FormatterPath_With_Known_Coordinates_Returns_Expected_Polyline() { + // Arrange + PolylineValueFormatter<(double Latitude, double Longitude)> valueFormatter = + FormatterBuilder<(double Latitude, double Longitude)>.Create() + .AddValue("lat", c => c.Latitude) + .AddValue("lon", c => c.Longitude) + .Build(); + + PolylineOptions<(double Latitude, double Longitude), string> options = new( + valueFormatter, + PolylineFormatter.ForString); + + AbstractPolylineEncoder<(double Latitude, double Longitude), string> encoder = new(options); + + (double Latitude, double Longitude)[] coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; + string expected = StaticValueProvider.Valid.GetPolyline(); + + // Act + string result = encoder.Encode(coordinates.AsSpan()); + + // Assert + Assert.AreEqual(expected, result); + } + + /// + /// Tests that the formatter-based encoder respects a non-zero baseline by subtracting it + /// from the first item's scaled value, producing a smaller first delta. + /// + [TestMethod] + public void Encode_FormatterPath_With_Baseline_Produces_Correct_First_Delta() { + // Arrange: a single coordinate at (1.0, 2.0) with precision 5. + // Without baseline, scaled values are (100000, 200000). + // With baseline lat=100000, the first lat delta must be 0 (100000 - 100000). + PolylineValueFormatter<(double Lat, double Lon)> valueFormatter = + FormatterBuilder<(double Lat, double Lon)>.Create() + .AddValue("lat", c => c.Lat) + .SetBaseline(100000L) // scaled value of 1.0 at precision 5 + .AddValue("lon", c => c.Lon) + .Build(); + + PolylineOptions<(double Lat, double Lon), string> optionsWithBaseline = new( + valueFormatter, + PolylineFormatter.ForString); + + PolylineValueFormatter<(double Lat, double Lon)> valueFormatterNoBaseline = + FormatterBuilder<(double Lat, double Lon)>.Create() + .AddValue("lat", c => c.Lat) + .AddValue("lon", c => c.Lon) + .Build(); + + PolylineOptions<(double Lat, double Lon), string> optionsNoBaseline = new( + valueFormatterNoBaseline, + PolylineFormatter.ForString); + + AbstractPolylineEncoder<(double Lat, double Lon), string> encoderWithBaseline = new(optionsWithBaseline); + AbstractPolylineEncoder<(double Lat, double Lon), string> encoderNoBaseline = new(optionsNoBaseline); + + (double, double)[] coordinates = [(1.0, 2.0)]; + + // Act + string resultWithBaseline = encoderWithBaseline.Encode(coordinates.AsSpan()); + string resultNoBaseline = encoderNoBaseline.Encode(coordinates.AsSpan()); + + // Assert: baseline shifts the first delta so the results differ, and the baseline result + // encodes a smaller (zero) latitude delta. + Assert.AreNotEqual(resultNoBaseline, resultWithBaseline); + } + + /// + /// Tests that the formatter-based encoder produces output that the formatter-based decoder can + /// reconstruct exactly (full round-trip). + /// + [TestMethod] + public void Encode_FormatterPath_RoundTrip_Produces_Original_Coordinates() { + // Arrange + PolylineValueFormatter<(double Latitude, double Longitude)> valueFormatter = + FormatterBuilder<(double Latitude, double Longitude)>.Create() + .AddValue("lat", c => c.Latitude) + .AddValue("lon", c => c.Longitude) + .WithCreate(static values => (values[0] / 1e5, values[1] / 1e5)) + .Build(); + + PolylineOptions<(double Latitude, double Longitude), string> options = new( + valueFormatter, + PolylineFormatter.ForString); + + AbstractPolylineEncoder<(double Latitude, double Longitude), string> encoder = new(options); + AbstractPolylineDecoder decoder = new(options); + + (double Latitude, double Longitude)[] original = [.. StaticValueProvider.Valid.GetCoordinates()]; + + // Act + string encoded = encoder.Encode(original.AsSpan()); + (double Latitude, double Longitude)[] decoded = [.. decoder.Decode(encoded)]; + + // Assert + Assert.AreEqual(original.Length, decoded.Length); + for (int i = 0; i < original.Length; i++) { + Assert.AreEqual(original[i].Latitude, decoded[i].Latitude, 1e-5); + Assert.AreEqual(original[i].Longitude, decoded[i].Longitude, 1e-5); + } + } } From a3777846e786e29ef373230785d7a1bc761b8476 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 02:06:28 +0000 Subject: [PATCH 06/22] Fix remaining wrong PolylineOptions type args in NotSupportedException messages in AbstractPolylineDecoder Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/f4994093-4128-4101-b5df-f6a031463712 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs index 5a637818..9dceb456 100644 --- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs +++ b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs @@ -264,7 +264,7 @@ protected virtual void ValidateFormat(ReadOnlyMemory sequence, ILogger? lo protected virtual ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline) { throw new NotSupportedException( $"Override {nameof(GetReadOnlyMemory)} in a derived class, or provide a " + - $"{nameof(PolylineOptions)} with a polyline formatter."); + $"{nameof(PolylineOptions)} with a polyline formatter."); } /// @@ -287,6 +287,6 @@ protected virtual ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline) protected virtual TCoordinate CreateCoordinate(double latitude, double longitude) { throw new NotSupportedException( $"Override {nameof(CreateCoordinate)} in a derived class, or provide a " + - $"{nameof(PolylineOptions)} with a value formatter."); + $"{nameof(PolylineOptions)} with a value formatter."); } } From 97098061c282e0ecfef0db94dfb028656b647ada Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:37:59 +0000 Subject: [PATCH 07/22] Updated docs for version 0.0 --- ....Abstraction.AbstractPolylineDecoder-2.yml | 107 +++++++- ....Abstraction.AbstractPolylineEncoder-2.yml | 115 ++++++++- ...gorithm.Abstraction.IPolylineDecoder-2.yml | 4 +- ...gorithm.Abstraction.IPolylineEncoder-2.yml | 4 +- ...rithm.Abstraction.IPolylineFormatter-1.yml | 105 ++++++++ ....Abstraction.IPolylineValueFormatter-1.yml | 153 +++++++++++ .../0.0/PolylineAlgorithm.Abstraction.yml | 16 ++ ...m.Extensions.PolylineDecoderExtensions.yml | 8 +- ...m.Extensions.PolylineEncoderExtensions.yml | 6 +- .../PolylineAlgorithm.FormatterBuilder-1.yml | 241 ++++++++++++++++++ ...lineAlgorithm.InvalidPolylineException.yml | 6 +- .../PolylineAlgorithm.PolylineEncoding.yml | 18 +- ...ylineAlgorithm.PolylineEncodingOptions.yml | 8 +- ...gorithm.PolylineEncodingOptionsBuilder.yml | 12 +- .../PolylineAlgorithm.PolylineFormatter.yml | 168 ++++++++++++ ...olylineAlgorithm.PolylineItemFactory-1.yml | 50 ++++ .../PolylineAlgorithm.PolylineOptions-2.yml | 157 ++++++++++++ ...lineAlgorithm.PolylineValueFormatter-1.yml | 226 ++++++++++++++++ api-reference/0.0/PolylineAlgorithm.yml | 33 +++ api-reference/0.0/toc.yml | 15 ++ 20 files changed, 1391 insertions(+), 61 deletions(-) create mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.FormatterBuilder-1.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineFormatter.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineValueFormatter-1.yml diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml index b5693066..8b4d27f8 100644 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml @@ -3,7 +3,7 @@ title: Class AbstractPolylineDecoder body: - api1: Class AbstractPolylineDecoder id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L22 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L31 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2 commentId: T:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2 @@ -15,7 +15,7 @@ body: - name: Assembly value: PolylineAlgorithm.dll - markdown: Provides a base implementation for decoding encoded polyline strings into sequences of geographic coordinates. -- code: 'public abstract class AbstractPolylineDecoder : IPolylineDecoder' +- code: 'public class AbstractPolylineDecoder : IPolylineDecoder' - h4: Type Parameters - parameters: - name: TPolyline @@ -50,13 +50,31 @@ body: url: https://learn.microsoft.com/dotnet/api/system.object.tostring - h2: Remarks - markdown: >- - Derive from this class to implement a decoder for a specific polyline type. Override +

- and to provide type-specific behavior. + Formatter-based use (no subclassing required): + + Supply a via the + + + + constructor. The formatters handle all type-specific concerns; override nothing. + +

+ +

+ + Legacy override-based use: + + Derive from this class and override and + + to provide type-specific behaviour. These overrides take priority over any registered formatter. + +

- h2: Constructors - api3: AbstractPolylineDecoder() id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2__ctor - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L28 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L39 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor @@ -64,7 +82,7 @@ body: - code: protected AbstractPolylineDecoder() - api3: AbstractPolylineDecoder(PolylineEncodingOptions) id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2__ctor_PolylineAlgorithm_PolylineEncodingOptions_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L40 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L51 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions) @@ -78,6 +96,46 @@ body: url: PolylineAlgorithm.PolylineEncodingOptions.html description: The to use for encoding operations. - h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when options is null. +- api3: AbstractPolylineDecoder(PolylineOptions) + id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2__ctor_PolylineAlgorithm_PolylineOptions__1__0__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L78 + metadata: + uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`1,`0}) + commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`1,`0}) +- markdown: >- + Initializes a new instance of the class + + using the supplied . +- code: public AbstractPolylineDecoder(PolylineOptions options) +- h4: Parameters +- parameters: + - name: options + type: + - text: PolylineOptions + url: PolylineAlgorithm.PolylineOptions-2.html + - < + - TCoordinate + - ',' + - " " + - TPolyline + - '>' + description: >- + A that carries both the value formatter and + + the polyline formatter together with the underlying . +- h4: Remarks +- markdown: >- + Use this constructor when you want formatter-driven decoding without subclassing. + + The and hooks are not called; + + all type-specific logic is delegated to the formatters. +- h4: Exceptions - parameters: - type: - text: ArgumentNullException @@ -86,7 +144,7 @@ body: - h2: Properties - api3: Options id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Options - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L54 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L94 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Options commentId: P:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Options @@ -100,12 +158,12 @@ body: - h2: Methods - api3: CreateCoordinate(double, double) id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_CreateCoordinate_System_Double_System_Double_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L202 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L286 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.CreateCoordinate(System.Double,System.Double) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.CreateCoordinate(System.Double,System.Double) - markdown: Creates a TCoordinate instance from the specified latitude and longitude values. -- code: protected abstract TCoordinate CreateCoordinate(double latitude, double longitude) +- code: protected virtual TCoordinate CreateCoordinate(double latitude, double longitude) - h4: Parameters - parameters: - name: latitude @@ -123,9 +181,18 @@ body: - type: - TCoordinate description: A TCoordinate instance representing the specified geographic coordinate. +- h4: Exceptions +- parameters: + - type: + - text: NotSupportedException + url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception + description: >- + Thrown by the default implementation when no value formatter is registered and the method + + has not been overridden in a derived class. - api3: Decode(TPolyline, CancellationToken) id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Decode__0_System_Threading_CancellationToken_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L81 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L121 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken) @@ -133,7 +200,10 @@ body: Decodes an encoded TPolyline into a sequence of TCoordinate instances, with support for cancellation. -- code: public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default) +- code: >- + [SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains two path implementations.")] + + public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default) - h4: Parameters - parameters: - name: polyline @@ -175,12 +245,12 @@ body: description: Thrown when cancellationToken is canceled during decoding. - api3: GetReadOnlyMemory(in TPolyline) id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_GetReadOnlyMemory__0__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L187 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L263 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.GetReadOnlyMemory(`0@) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.GetReadOnlyMemory(`0@) - markdown: Extracts the underlying read-only memory region of characters from the specified polyline instance. -- code: protected abstract ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline) +- code: protected virtual ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline) - h4: Parameters - parameters: - name: polyline @@ -197,9 +267,18 @@ body: url: https://learn.microsoft.com/dotnet/api/system.char - '>' description: A of representing the encoded polyline characters. +- h4: Exceptions +- parameters: + - type: + - text: NotSupportedException + url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception + description: >- + Thrown by the default implementation when no polyline formatter is registered and the method + + has not been overridden in a derived class. - api3: ValidateFormat(ReadOnlyMemory, ILogger?) id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_ValidateFormat_System_ReadOnlyMemory_System_Char__Microsoft_Extensions_Logging_ILogger_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L167 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L239 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.ValidateFormat(System.ReadOnlyMemory{System.Char},Microsoft.Extensions.Logging.ILogger) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.ValidateFormat(System.ReadOnlyMemory{System.Char},Microsoft.Extensions.Logging.ILogger) diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml index 64e97b11..ea9654c1 100644 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml @@ -3,7 +3,7 @@ title: Class AbstractPolylineEncoder body: - api1: Class AbstractPolylineEncoder id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L27 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L37 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2 commentId: T:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2 @@ -15,7 +15,7 @@ body: - name: Assembly value: PolylineAlgorithm.dll - markdown: Provides a base implementation for encoding sequences of geographic coordinates into encoded polyline strings. -- code: 'public abstract class AbstractPolylineEncoder : IPolylineEncoder' +- code: 'public class AbstractPolylineEncoder : IPolylineEncoder' - h4: Type Parameters - parameters: - name: TCoordinate @@ -56,13 +56,33 @@ body: url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___ - h2: Remarks - markdown: >- - Derive from this class to implement an encoder for a specific coordinate and polyline type. Override +

- , , and to provide type-specific behavior. + Formatter-based use (no subclassing required): + + Supply a via the + + + + constructor. The formatter handles all type-specific concerns; override nothing. + +

+ +

+ + Legacy override-based use: + + Derive from this class and override , , + + and to provide type-specific behaviour. These overrides take + + priority over any registered formatter. + +

- h2: Constructors - api3: AbstractPolylineEncoder() id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2__ctor - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L33 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L45 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor @@ -70,7 +90,7 @@ body: - code: protected AbstractPolylineEncoder() - api3: AbstractPolylineEncoder(PolylineEncodingOptions) id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2__ctor_PolylineAlgorithm_PolylineEncodingOptions_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L43 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L55 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions) @@ -89,10 +109,50 @@ body: - text: ArgumentNullException url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception description: Thrown when options is null +- api3: AbstractPolylineEncoder(PolylineOptions) + id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2__ctor_PolylineAlgorithm_PolylineOptions__0__1__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L82 + metadata: + uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`0,`1}) + commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`0,`1}) +- markdown: >- + Initializes a new instance of the class + + using the supplied . +- code: public AbstractPolylineEncoder(PolylineOptions options) +- h4: Parameters +- parameters: + - name: options + type: + - text: PolylineOptions + url: PolylineAlgorithm.PolylineOptions-2.html + - < + - TCoordinate + - ',' + - " " + - TPolyline + - '>' + description: >- + A that carries both the value formatter and + + the polyline formatter together with the underlying . +- h4: Remarks +- markdown: >- + Use this constructor when you want formatter-driven encoding without subclassing. + + The , , and hooks + + are not called; all type-specific logic is delegated to the formatters. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when options is null. - h2: Properties - api3: Options id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Options - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L57 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L98 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Options commentId: P:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Options @@ -106,12 +166,12 @@ body: - h2: Methods - api3: CreatePolyline(ReadOnlyMemory) id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_CreatePolyline_System_ReadOnlyMemory_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L174 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L266 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.CreatePolyline(System.ReadOnlyMemory{System.Char}) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.CreatePolyline(System.ReadOnlyMemory{System.Char}) - markdown: Creates a polyline instance from the provided read-only sequence of characters. -- code: protected abstract TPolyline CreatePolyline(ReadOnlyMemory polyline) +- code: protected virtual TPolyline CreatePolyline(ReadOnlyMemory polyline) - h4: Parameters - parameters: - name: polyline @@ -128,9 +188,18 @@ body: - type: - TPolyline description: An instance of TPolyline representing the encoded polyline. +- h4: Exceptions +- parameters: + - type: + - text: NotSupportedException + url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception + description: >- + Thrown by the default implementation when no polyline formatter is registered and the method + + has not been overridden in a derived class. - api3: Encode(ReadOnlySpan, CancellationToken) id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Encode_System_ReadOnlySpan__0__System_Threading_CancellationToken_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L80 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L121 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) @@ -176,12 +245,12 @@ body: description: Thrown when the internal encoding buffer cannot accommodate the encoded value. - api3: GetLatitude(TCoordinate) id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_GetLatitude__0_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L194 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L302 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLatitude(`0) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLatitude(`0) - markdown: Extracts the latitude value from the specified coordinate. -- code: protected abstract double GetLatitude(TCoordinate current) +- code: protected virtual double GetLatitude(TCoordinate current) - h4: Parameters - parameters: - name: current @@ -194,14 +263,23 @@ body: - text: double url: https://learn.microsoft.com/dotnet/api/system.double description: The latitude value as a . +- h4: Exceptions +- parameters: + - type: + - text: NotSupportedException + url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception + description: >- + Thrown by the default implementation when no value formatter is registered and the method + + has not been overridden in a derived class. - api3: GetLongitude(TCoordinate) id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_GetLongitude__0_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L184 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L284 metadata: uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLongitude(`0) commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLongitude(`0) - markdown: Extracts the longitude value from the specified coordinate. -- code: protected abstract double GetLongitude(TCoordinate current) +- code: protected virtual double GetLongitude(TCoordinate current) - h4: Parameters - parameters: - name: current @@ -214,6 +292,15 @@ body: - text: double url: https://learn.microsoft.com/dotnet/api/system.double description: The longitude value as a . +- h4: Exceptions +- parameters: + - type: + - text: NotSupportedException + url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception + description: >- + Thrown by the default implementation when no value formatter is registered and the method + + has not been overridden in a derived class. languageId: csharp metadata: description: Provides a base implementation for encoding sequences of geographic coordinates into encoded polyline strings. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml index 733987cb..d4c991f8 100644 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml @@ -3,7 +3,7 @@ title: Interface IPolylineDecoder body: - api1: Interface IPolylineDecoder id: PolylineAlgorithm_Abstraction_IPolylineDecoder_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L22 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L22 metadata: uid: PolylineAlgorithm.Abstraction.IPolylineDecoder`2 commentId: T:PolylineAlgorithm.Abstraction.IPolylineDecoder`2 @@ -31,7 +31,7 @@ body: - h2: Methods - api3: Decode(TPolyline, CancellationToken) id: PolylineAlgorithm_Abstraction_IPolylineDecoder_2_Decode__0_System_Threading_CancellationToken_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L48 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L48 metadata: uid: PolylineAlgorithm.Abstraction.IPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken) commentId: M:PolylineAlgorithm.Abstraction.IPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken) diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml index 341a9de7..171811e9 100644 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml @@ -3,7 +3,7 @@ title: Interface IPolylineEncoder body: - api1: Interface IPolylineEncoder id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L36 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L36 metadata: uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2 commentId: T:PolylineAlgorithm.Abstraction.IPolylineEncoder`2 @@ -63,7 +63,7 @@ body: - h2: Methods - api3: Encode(ReadOnlySpan, CancellationToken) id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2_Encode_System_ReadOnlySpan__0__System_Threading_CancellationToken_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L76 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L76 metadata: uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) commentId: M:PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml new file mode 100644 index 00000000..c1d7296b --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml @@ -0,0 +1,105 @@ +### YamlMime:ApiPage +title: Interface IPolylineFormatter +body: +- api1: Interface IPolylineFormatter + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_1 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L28 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`1 + commentId: T:PolylineAlgorithm.Abstraction.IPolylineFormatter`1 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm.Abstraction + url: PolylineAlgorithm.Abstraction.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Defines how to produce a TPolyline from an encoded character buffer (output/write + + direction), and how to extract that buffer back from a TPolyline (input/read + + direction). +- code: public interface IPolylineFormatter +- h4: Type Parameters +- parameters: + - name: TPolyline + description: >- + The polyline surface type — for example or + of . +- h2: Remarks +- markdown: >- +

+ + This interface is the polyline-surface counterpart to . + + The engine exclusively works with of internally. + + The formatter is the only code that touches TPolyline. + +

+ +

+ + Use , , or + + to obtain a ready-made implementation. + +

+- h2: Methods +- api3: Read(TPolyline) + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_1_Read__0_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L41 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`1.Read(`0) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`1.Read(`0) +- markdown: Extracts the character buffer from a TPolyline for the decoder to read. +- code: ReadOnlyMemory Read(TPolyline polyline) +- h4: Parameters +- parameters: + - name: polyline + type: + - TPolyline + description: The polyline to read from. +- h4: Returns +- parameters: + - type: + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: A of representing the encoded characters. +- api3: Write(ReadOnlyMemory) + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_1_Write_System_ReadOnlyMemory_System_Char__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L34 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`1.Write(System.ReadOnlyMemory{System.Char}) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`1.Write(System.ReadOnlyMemory{System.Char}) +- markdown: Creates a TPolyline from the encoded character buffer produced by the encoder. +- code: TPolyline Write(ReadOnlyMemory encoded) +- h4: Parameters +- parameters: + - name: encoded + type: + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: The encoded polyline as a read-only span of characters. +- h4: Returns +- parameters: + - type: + - TPolyline + description: A TPolyline wrapping or derived from encoded. +languageId: csharp +metadata: + description: >- + Defines how to produce a TPolyline from an encoded character buffer (output/write + + direction), and how to extract that buffer back from a TPolyline (input/read + + direction). diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml new file mode 100644 index 00000000..0f5297f3 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml @@ -0,0 +1,153 @@ +### YamlMime:ApiPage +title: Interface IPolylineValueFormatter +body: +- api1: Interface IPolylineValueFormatter + id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L26 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1 + commentId: T:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm.Abstraction + url: PolylineAlgorithm.Abstraction.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Defines how to extract scaled numeric values from a TValue during encoding, and + + how to reconstruct a TValue from those values during decoding. +- code: public interface IPolylineValueFormatter +- h4: Type Parameters +- parameters: + - name: TValue + description: The coordinate or item type that the formatter understands. +- h2: Remarks +- markdown: >- +

+ + This interface is the coordinate-side counterpart to . + + Together they allow the engine base classes to be used directly — without subclassing — by supplying + + both formatters via . + +

+ +

+ + Use to build a that + + already implements this interface. + +

+- h2: Properties +- api3: Width + id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1_Width + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L54 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.Width + commentId: P:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.Width +- markdown: >- + Gets the number of values (columns) per encoded item. + + This is the required length of the buffer passed to and + + the length of the span received in . +- code: int Width { get; } +- h4: Property Value +- parameters: + - type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 +- h2: Methods +- api3: CreateItem(ReadOnlySpan) + id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1_CreateItem_System_ReadOnlySpan_System_Int64__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L47 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.CreateItem(System.ReadOnlySpan{System.Int64}) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.CreateItem(System.ReadOnlySpan{System.Int64}) +- markdown: >- + Reconstructs a TValue from the given scaled integer values. + + Called once per decoded item in the decoding loop. +- code: TValue CreateItem(ReadOnlySpan values) +- h4: Parameters +- parameters: + - name: values + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: >- + The accumulated scaled integer values decoded from the polyline. Each element corresponds to + + the same column position as in . +- h4: Returns +- parameters: + - type: + - TValue + description: A TValue reconstructed from values. +- api3: GetBaseline(int) + id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1_GetBaseline_System_Int32_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L62 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.GetBaseline(System.Int32) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.GetBaseline(System.Int32) +- markdown: >- + Returns the baseline (epoch) for the column at index, or 0 if none is configured. + + The encoder subtracts this value from the first item's scaled column value to keep the initial delta small. +- code: long GetBaseline(int index) +- h4: Parameters +- parameters: + - name: index + type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + description: The zero-based column index. Must be in the range [0, ). +- h4: Returns +- parameters: + - type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: The baseline value, or 0 when no baseline has been defined for the column. +- api3: GetValues(TValue, Span) + id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1_GetValues__0_System_Span_System_Int64__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L36 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.GetValues(`0,System.Span{System.Int64}) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.GetValues(`0,System.Span{System.Int64}) +- markdown: >- + Extracts and scales all column values from item into the values span. + + Called once per item in the encoding loop. +- code: void GetValues(TValue item, Span values) +- h4: Parameters +- parameters: + - name: item + type: + - TValue + description: The source item from which column values are extracted. + - name: values + type: + - text: Span + url: https://learn.microsoft.com/dotnet/api/system.span-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: >- + Output buffer that receives the scaled integer values. Its length must equal the number of columns + + defined by this formatter. +languageId: csharp +metadata: + description: >- + Defines how to extract scaled numeric values from a TValue during encoding, and + + how to reconstruct a TValue from those values during decoding. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.yml index e9de48f7..ff64bf1c 100644 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.yml +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.yml @@ -31,4 +31,20 @@ body: Implementations interpret the generic TValue type and produce an encoded representation of those coordinates as TPolyline. + - type: + text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html + description: >- + Defines how to produce a TPolyline from an encoded character buffer (output/write + + direction), and how to extract that buffer back from a TPolyline (input/read + + direction). + - type: + text: IPolylineValueFormatter + url: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.html + description: >- + Defines how to extract scaled numeric values from a TValue during encoding, and + + how to reconstruct a TValue from those values during decoding. languageId: csharp diff --git a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml b/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml index 8356b63c..716776cf 100644 --- a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml +++ b/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml @@ -3,7 +3,7 @@ title: Class PolylineDecoderExtensions body: - api1: Class PolylineDecoderExtensions id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L16 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L16 metadata: uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions commentId: T:PolylineAlgorithm.Extensions.PolylineDecoderExtensions @@ -41,7 +41,7 @@ body: - h2: Methods - api3: Decode(IPolylineDecoder, char[]) id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode__1_PolylineAlgorithm_Abstraction_IPolylineDecoder_System_String___0__System_Char___ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L33 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L33 metadata: uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.String,``0},System.Char[]) commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.String,``0},System.Char[]) @@ -89,7 +89,7 @@ body: description: Thrown when decoder or polyline is null. - api3: Decode(IPolylineDecoder, ReadOnlyMemory) id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode__1_PolylineAlgorithm_Abstraction_IPolylineDecoder_System_String___0__System_ReadOnlyMemory_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L61 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L61 metadata: uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.String,``0},System.ReadOnlyMemory{System.Char}) commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.String,``0},System.ReadOnlyMemory{System.Char}) @@ -139,7 +139,7 @@ body: description: Thrown when decoder is null. - api3: Decode(IPolylineDecoder, TValue>, string) id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode__1_PolylineAlgorithm_Abstraction_IPolylineDecoder_System_ReadOnlyMemory_System_Char____0__System_String_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L86 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L86 metadata: uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.ReadOnlyMemory{System.Char},``0},System.String) commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.ReadOnlyMemory{System.Char},``0},System.String) diff --git a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml b/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml index e5296518..36fd5ef9 100644 --- a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml +++ b/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml @@ -3,7 +3,7 @@ title: Class PolylineEncoderExtensions body: - api1: Class PolylineEncoderExtensions id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L19 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L19 metadata: uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions commentId: T:PolylineAlgorithm.Extensions.PolylineEncoderExtensions @@ -41,7 +41,7 @@ body: - h2: Methods - api3: Encode(IPolylineEncoder, List) id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1__System_Collections_Generic_List___0__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L37 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L37 metadata: uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},System.Collections.Generic.List{``0}) commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},System.Collections.Generic.List{``0}) @@ -92,7 +92,7 @@ body: description: Thrown when encoder or coordinates is null. - api3: Encode(IPolylineEncoder, TCoordinate[]) id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L73 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L73 metadata: uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},``0[]) commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},``0[]) diff --git a/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-1.yml b/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-1.yml new file mode 100644 index 00000000..4fd394df --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-1.yml @@ -0,0 +1,241 @@ +### YamlMime:ApiPage +title: Class FormatterBuilder +body: +- api1: Class FormatterBuilder + id: PolylineAlgorithm_FormatterBuilder_1 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L28 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`1 + commentId: T:PolylineAlgorithm.FormatterBuilder`1 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Provides a fluent builder for constructing a . +- code: public sealed class FormatterBuilder +- h4: Type Parameters +- parameters: + - name: T + description: The source object type from which column values are extracted. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-1.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- +

+ + Use to obtain an instance, call once per column, + + optionally chain to specify an epoch for the most-recently added column, + + optionally chain to register a factory for the decoding direction, + + then call to produce the immutable . + +

+ +

+ + The builder is the only way to create a — its + + constructor is internal. + +

+- h2: Methods +- api3: AddValue(string, Func, uint) + id: PolylineAlgorithm_FormatterBuilder_1_AddValue_System_String_System_Func__0_System_Double__System_UInt32_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L62 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`1.AddValue(System.String,System.Func{`0,System.Double},System.UInt32) + commentId: M:PolylineAlgorithm.FormatterBuilder`1.AddValue(System.String,System.Func{`0,System.Double},System.UInt32) +- markdown: Adds a column with the specified value selector and precision. +- code: public FormatterBuilder AddValue(string name, Func selector, uint precision = 5) +- h4: Parameters +- parameters: + - name: name + type: + - text: string + url: https://learn.microsoft.com/dotnet/api/system.string + description: A unique, non-null, non-empty name that identifies the column. Used for diagnostics only. + - name: selector + type: + - text: Func + url: https://learn.microsoft.com/dotnet/api/system.func-2 + - < + - T + - ',' + - " " + - text: double + url: https://learn.microsoft.com/dotnet/api/system.double + - '>' + description: >- + A delegate that extracts the column's raw value from an item of type + + T. + - name: precision + type: + - text: uint + url: https://learn.microsoft.com/dotnet/api/system.uint32 + description: >- + The number of decimal places to preserve. Each extracted value is multiplied by + + 10^precision before encoding. Defaults to 5. + optional: true +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-1.html + - < + - T + - '>' + description: The current instance for method chaining. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when name or selector is null. + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when name is empty, or a rule with the same name already exists. +- api3: Build() + id: PolylineAlgorithm_FormatterBuilder_1_Build + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L137 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`1.Build + commentId: M:PolylineAlgorithm.FormatterBuilder`1.Build +- markdown: Bakes all added rules into a sealed, immutable . +- code: public PolylineValueFormatter Build() +- h4: Returns +- parameters: + - type: + - text: PolylineValueFormatter + url: PolylineAlgorithm.PolylineValueFormatter-1.html + - < + - T + - '>' + description: An immutable whose rules can no longer be changed. +- h4: Exceptions +- parameters: + - type: + - text: InvalidOperationException + url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception + description: Thrown when no rules have been added. +- api3: Create() + id: PolylineAlgorithm_FormatterBuilder_1_Create + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L39 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`1.Create + commentId: M:PolylineAlgorithm.FormatterBuilder`1.Create +- markdown: Creates a new instance. +- code: public static FormatterBuilder Create() +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-1.html + - < + - T + - '>' + description: A fresh with no rules. +- api3: SetBaseline(long) + id: PolylineAlgorithm_FormatterBuilder_1_SetBaseline_System_Int64_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L94 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`1.SetBaseline(System.Int64) + commentId: M:PolylineAlgorithm.FormatterBuilder`1.SetBaseline(System.Int64) +- markdown: >- + Sets a baseline (epoch) on the most-recently added column. + + During encoding the baseline is subtracted from the first item's scaled column value, + + keeping the initial delta small when the absolute first value is large. +- code: public FormatterBuilder SetBaseline(long baseline) +- h4: Parameters +- parameters: + - name: baseline + type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: The baseline value to apply to the first item's column value. +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-1.html + - < + - T + - '>' + description: The current instance for method chaining. +- h4: Exceptions +- parameters: + - type: + - text: InvalidOperationException + url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception + description: Thrown when no rules have been added yet. Call before . +- api3: WithCreate(PolylineItemFactory) + id: PolylineAlgorithm_FormatterBuilder_1_WithCreate_PolylineAlgorithm_PolylineItemFactory__0__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L118 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`1.WithCreate(PolylineAlgorithm.PolylineItemFactory{`0}) + commentId: M:PolylineAlgorithm.FormatterBuilder`1.WithCreate(PolylineAlgorithm.PolylineItemFactory{`0}) +- markdown: >- + Registers a factory delegate used to reconstruct a T from scaled values + + during decoding. This enables the decoding direction of . +- code: public FormatterBuilder WithCreate(PolylineItemFactory create) +- h4: Parameters +- parameters: + - name: create + type: + - text: PolylineItemFactory + url: PolylineAlgorithm.PolylineItemFactory-1.html + - < + - T + - '>' + description: >- + A delegate that accepts the scaled integer values decoded from the polyline and returns a + + T. The span length always equals the number of columns added via + + . +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-1.html + - < + - T + - '>' + description: The current instance for method chaining. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when create is null. +languageId: csharp +metadata: + description: Provides a fluent builder for constructing a . diff --git a/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml b/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml index 2c4e3cc1..7bc75368 100644 --- a/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml +++ b/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml @@ -3,7 +3,7 @@ title: Class InvalidPolylineException body: - api1: Class InvalidPolylineException id: PolylineAlgorithm_InvalidPolylineException - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/InvalidPolylineException.cs#L17 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L17 metadata: uid: PolylineAlgorithm.InvalidPolylineException commentId: T:PolylineAlgorithm.InvalidPolylineException @@ -71,7 +71,7 @@ body: - h2: Constructors - api3: InvalidPolylineException() id: PolylineAlgorithm_InvalidPolylineException__ctor - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/InvalidPolylineException.cs#L22 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L22 metadata: uid: PolylineAlgorithm.InvalidPolylineException.#ctor commentId: M:PolylineAlgorithm.InvalidPolylineException.#ctor @@ -79,7 +79,7 @@ body: - code: public InvalidPolylineException() - api3: InvalidPolylineException(string, Exception) id: PolylineAlgorithm_InvalidPolylineException__ctor_System_String_System_Exception_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/InvalidPolylineException.cs#L43 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L43 metadata: uid: PolylineAlgorithm.InvalidPolylineException.#ctor(System.String,System.Exception) commentId: M:PolylineAlgorithm.InvalidPolylineException.#ctor(System.String,System.Exception) diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml index 1add8147..7eb6fc73 100644 --- a/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml +++ b/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml @@ -3,7 +3,7 @@ title: Class PolylineEncoding body: - api1: Class PolylineEncoding id: PolylineAlgorithm_PolylineEncoding - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L23 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L23 metadata: uid: PolylineAlgorithm.PolylineEncoding commentId: T:PolylineAlgorithm.PolylineEncoding @@ -50,7 +50,7 @@ body: - h2: Methods - api3: Denormalize(int, uint) id: PolylineAlgorithm_PolylineEncoding_Denormalize_System_Int32_System_UInt32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L122 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L122 metadata: uid: PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int32,System.UInt32) commentId: M:PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int32,System.UInt32) @@ -117,7 +117,7 @@ body: description: Thrown if the arithmetic operation overflows during conversion. - api3: GetRequiredBufferSize(int) id: PolylineAlgorithm_PolylineEncoding_GetRequiredBufferSize_System_Int32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L298 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L298 metadata: uid: PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(System.Int32) commentId: M:PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(System.Int32) @@ -200,7 +200,7 @@ body: - ) - api3: Normalize(double, uint) id: PolylineAlgorithm_PolylineEncoding_Normalize_System_Double_System_UInt32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L61 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L61 metadata: uid: PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,System.UInt32) commentId: M:PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,System.UInt32) @@ -268,7 +268,7 @@ body: description: Thrown when the normalized result exceeds the range of a 32-bit signed integer during the conversion from double to int. - api3: TryReadValue(ref int, ReadOnlyMemory, ref int) id: PolylineAlgorithm_PolylineEncoding_TryReadValue_System_Int32__System_ReadOnlyMemory_System_Char__System_Int32__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L169 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L169 metadata: uid: PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int32@,System.ReadOnlyMemory{System.Char},System.Int32@) commentId: M:PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int32@,System.ReadOnlyMemory{System.Char},System.Int32@) @@ -328,7 +328,7 @@ body:

- api3: TryWriteValue(int, Span, ref int) id: PolylineAlgorithm_PolylineEncoding_TryWriteValue_System_Int32_System_Span_System_Char__System_Int32__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L237 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L237 metadata: uid: PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int32,System.Span{System.Char},System.Int32@) commentId: M:PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int32,System.Span{System.Char},System.Int32@) @@ -407,7 +407,7 @@ body:

- api3: ValidateBlockLength(ReadOnlySpan) id: PolylineAlgorithm_PolylineEncoding_ValidateBlockLength_System_ReadOnlySpan_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L438 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L438 metadata: uid: PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan{System.Char}) commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan{System.Char}) @@ -441,7 +441,7 @@ body: description: Thrown when a block exceeds 7 characters or the polyline does not end with a valid block terminator. - api3: ValidateCharRange(ReadOnlySpan) id: PolylineAlgorithm_PolylineEncoding_ValidateCharRange_System_ReadOnlySpan_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L392 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L392 metadata: uid: PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan{System.Char}) commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan{System.Char}) @@ -479,7 +479,7 @@ body: description: Thrown when an invalid character is found in the polyline segment. - api3: ValidateFormat(ReadOnlySpan) id: PolylineAlgorithm_PolylineEncoding_ValidateFormat_System_ReadOnlySpan_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncoding.cs#L370 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L370 metadata: uid: PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan{System.Char}) commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan{System.Char}) diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions.yml index 1947eac1..b257b79a 100644 --- a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions.yml +++ b/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions.yml @@ -3,7 +3,7 @@ title: Class PolylineEncodingOptions body: - api1: Class PolylineEncodingOptions id: PolylineAlgorithm_PolylineEncodingOptions - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L29 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L29 metadata: uid: PolylineAlgorithm.PolylineEncodingOptions commentId: T:PolylineAlgorithm.PolylineEncodingOptions @@ -54,7 +54,7 @@ body: - h2: Properties - api3: LoggerFactory id: PolylineAlgorithm_PolylineEncodingOptions_LoggerFactory - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L41 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L41 metadata: uid: PolylineAlgorithm.PolylineEncodingOptions.LoggerFactory commentId: P:PolylineAlgorithm.PolylineEncodingOptions.LoggerFactory @@ -72,7 +72,7 @@ body: To enable logging, provide a custom implementation. - api3: Precision id: PolylineAlgorithm_PolylineEncodingOptions_Precision - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L60 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L60 metadata: uid: PolylineAlgorithm.PolylineEncodingOptions.Precision commentId: P:PolylineAlgorithm.PolylineEncodingOptions.Precision @@ -104,7 +104,7 @@ body:

- api3: StackAllocLimit id: PolylineAlgorithm_PolylineEncodingOptions_StackAllocLimit - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L73 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L73 metadata: uid: PolylineAlgorithm.PolylineEncodingOptions.StackAllocLimit commentId: P:PolylineAlgorithm.PolylineEncodingOptions.StackAllocLimit diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml index 66ae63b8..291ae3d6 100644 --- a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml +++ b/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml @@ -3,7 +3,7 @@ title: Class PolylineEncodingOptionsBuilder body: - api1: Class PolylineEncodingOptionsBuilder id: PolylineAlgorithm_PolylineEncodingOptionsBuilder - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L15 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L15 metadata: uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder commentId: T:PolylineAlgorithm.PolylineEncodingOptionsBuilder @@ -39,7 +39,7 @@ body: - h2: Methods - api3: Build() id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_Build - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L38 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L38 metadata: uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.Build commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.Build @@ -53,7 +53,7 @@ body: description: A configured instance. - api3: Create() id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_Create - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L28 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L28 metadata: uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.Create commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.Create @@ -67,7 +67,7 @@ body: description: An instance for configuring polyline encoding options. - api3: WithLoggerFactory(ILoggerFactory) id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithLoggerFactory_Microsoft_Extensions_Logging_ILoggerFactory_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L97 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L97 metadata: uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithLoggerFactory(Microsoft.Extensions.Logging.ILoggerFactory) commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithLoggerFactory(Microsoft.Extensions.Logging.ILoggerFactory) @@ -88,7 +88,7 @@ body: description: The current instance for method chaining. - api3: WithPrecision(uint) id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithPrecision_System_UInt32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L82 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L82 metadata: uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithPrecision(System.UInt32) commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithPrecision(System.UInt32) @@ -109,7 +109,7 @@ body: description: The current instance for method chaining. - api3: WithStackAllocLimit(int) id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithStackAllocLimit_System_Int32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/review-github-templates/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L61 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L61 metadata: uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithStackAllocLimit(System.Int32) commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithStackAllocLimit(System.Int32) diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineFormatter.yml b/api-reference/0.0/PolylineAlgorithm.PolylineFormatter.yml new file mode 100644 index 00000000..8c385549 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineFormatter.yml @@ -0,0 +1,168 @@ +### YamlMime:ApiPage +title: Class PolylineFormatter +body: +- api1: Class PolylineFormatter + id: PolylineAlgorithm_PolylineFormatter + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L22 + metadata: + uid: PolylineAlgorithm.PolylineFormatter + commentId: T:PolylineAlgorithm.PolylineFormatter +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Provides static factory methods and ready-made instances of + + for the most common polyline surface types. +- code: public static class PolylineFormatter +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineFormatter + url: PolylineAlgorithm.PolylineFormatter.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.MemberwiseClone() + url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- +

+ + Use or for the two most common cases. + + Call to build a custom formatter from a pair of delegates. + +

+- h2: Properties +- api3: ForMemory + id: PolylineAlgorithm_PolylineFormatter_ForMemory + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L36 + metadata: + uid: PolylineAlgorithm.PolylineFormatter.ForMemory + commentId: P:PolylineAlgorithm.PolylineFormatter.ForMemory +- markdown: >- + Gets a pass-through formatter for of . + + Both Write and Read are identity operations. +- code: public static IPolylineFormatter> ForMemory { get; } +- h4: Property Value +- parameters: + - type: + - text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html + - < + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + - '>' +- api3: ForString + id: PolylineAlgorithm_PolylineFormatter_ForString + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L27 + metadata: + uid: PolylineAlgorithm.PolylineFormatter.ForString + commentId: P:PolylineAlgorithm.PolylineFormatter.ForString +- markdown: >- + Gets a formatter that produces a from the encoded char buffer and reads + + the buffer back via string.AsMemory(). +- code: public static IPolylineFormatter ForString { get; } +- h4: Property Value +- parameters: + - type: + - text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html + - < + - text: string + url: https://learn.microsoft.com/dotnet/api/system.string + - '>' +- h2: Methods +- api3: Create(Func, T>, Func>) + id: PolylineAlgorithm_PolylineFormatter_Create__1_System_Func_System_ReadOnlyMemory_System_Char____0__System_Func___0_System_ReadOnlyMemory_System_Char___ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L56 + metadata: + uid: PolylineAlgorithm.PolylineFormatter.Create``1(System.Func{System.ReadOnlyMemory{System.Char},``0},System.Func{``0,System.ReadOnlyMemory{System.Char}}) + commentId: M:PolylineAlgorithm.PolylineFormatter.Create``1(System.Func{System.ReadOnlyMemory{System.Char},``0},System.Func{``0,System.ReadOnlyMemory{System.Char}}) +- markdown: Creates a custom from a pair of delegates. +- code: public static IPolylineFormatter Create(Func, T> write, Func> read) +- h4: Parameters +- parameters: + - name: write + type: + - text: Func + url: https://learn.microsoft.com/dotnet/api/system.func-2 + - < + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + - ',' + - " " + - T + - '>' + description: >- + Converts the encoded of produced by the encoder + + into a T. + - name: read + type: + - text: Func + url: https://learn.microsoft.com/dotnet/api/system.func-2 + - < + - T + - ',' + - " " + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + - '>' + description: Extracts the encoded character buffer from a T for the decoder to consume. +- h4: Returns +- parameters: + - type: + - text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html + - < + - T + - '>' + description: A sealed backed by the supplied delegates. +- h4: Type Parameters +- parameters: + - name: T + description: The polyline surface type. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when write or read is null. +languageId: csharp +metadata: + description: >- + Provides static factory methods and ready-made instances of + + for the most common polyline surface types. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml b/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml new file mode 100644 index 00000000..4420f1d4 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml @@ -0,0 +1,50 @@ +### YamlMime:ApiPage +title: Delegate PolylineItemFactory +body: +- api1: Delegate PolylineItemFactory + id: PolylineAlgorithm_PolylineItemFactory_1 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineItemFactory.cs#L20 + metadata: + uid: PolylineAlgorithm.PolylineItemFactory`1 + commentId: T:PolylineAlgorithm.PolylineItemFactory`1 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Represents a factory method that reconstructs a T item from an array of + + scaled integer values decoded from a polyline. +- code: public delegate T PolylineItemFactory(ReadOnlySpan values) +- h4: Parameters +- parameters: + - name: values + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: >- + The scaled integer values accumulated from the polyline decoder. Each element corresponds to one + + column as defined by the that built the associated formatter. +- h4: Returns +- parameters: + - type: + - T + description: A T instance reconstructed from values. +- h4: Type Parameters +- parameters: + - name: T + description: The coordinate or item type to create. +languageId: csharp +metadata: + description: >- + Represents a factory method that reconstructs a T item from an array of + + scaled integer values decoded from a polyline. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml b/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml new file mode 100644 index 00000000..b7dade96 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml @@ -0,0 +1,157 @@ +### YamlMime:ApiPage +title: Class PolylineOptions +body: +- api1: Class PolylineOptions + id: PolylineAlgorithm_PolylineOptions_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L22 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2 + commentId: T:PolylineAlgorithm.PolylineOptions`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Provides unified configuration for a formatter-driven encoding or decoding operation. +- code: public sealed class PolylineOptions +- h4: Type Parameters +- parameters: + - name: TValue + description: The coordinate or item type understood by the value formatter. + - name: TPolyline + description: The polyline surface type understood by the polyline formatter. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineOptions + url: PolylineAlgorithm.PolylineOptions-2.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- + Combines an (which defines the column schema, scaling + + rules, and item factory) with an (which converts between + + the raw character buffer and the surface type) and a (which + + controls buffer sizes, precision for legacy paths, and logging). +- h2: Constructors +- api3: PolylineOptions(IPolylineValueFormatter, IPolylineFormatter, PolylineEncodingOptions?) + id: PolylineAlgorithm_PolylineOptions_2__ctor_PolylineAlgorithm_Abstraction_IPolylineValueFormatter__0__PolylineAlgorithm_Abstraction_IPolylineFormatter__1__PolylineAlgorithm_PolylineEncodingOptions_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L42 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2.#ctor(PolylineAlgorithm.Abstraction.IPolylineValueFormatter{`0},PolylineAlgorithm.Abstraction.IPolylineFormatter{`1},PolylineAlgorithm.PolylineEncodingOptions) + commentId: M:PolylineAlgorithm.PolylineOptions`2.#ctor(PolylineAlgorithm.Abstraction.IPolylineValueFormatter{`0},PolylineAlgorithm.Abstraction.IPolylineFormatter{`1},PolylineAlgorithm.PolylineEncodingOptions) +- markdown: Initializes a new instance of . +- code: public PolylineOptions(IPolylineValueFormatter valueFormatter, IPolylineFormatter polylineFormatter, PolylineEncodingOptions? encoding = null) +- h4: Parameters +- parameters: + - name: valueFormatter + type: + - text: IPolylineValueFormatter + url: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.html + - < + - TValue + - '>' + description: >- + The formatter that defines the column schema, scaling rules, and item factory. Must not be + + null. + - name: polylineFormatter + type: + - text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html + - < + - TPolyline + - '>' + description: >- + The formatter that converts between the raw character buffer and + + TPolyline. Must not be null. + - name: encoding + type: + - text: PolylineEncodingOptions + url: PolylineAlgorithm.PolylineEncodingOptions.html + - '?' + description: >- + The encoding options that control buffer sizes, precision, and logging. + + Pass null to use default options. + optional: true +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: >- + Thrown when valueFormatter or polylineFormatter is + + null. +- h2: Properties +- api3: Encoding + id: PolylineAlgorithm_PolylineOptions_2_Encoding + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L73 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2.Encoding + commentId: P:PolylineAlgorithm.PolylineOptions`2.Encoding +- markdown: Gets the encoding options that control buffer sizes, precision, and logging. +- code: public PolylineEncodingOptions Encoding { get; } +- h4: Property Value +- parameters: + - type: + - text: PolylineEncodingOptions + url: PolylineAlgorithm.PolylineEncodingOptions.html +- api3: PolylineFormatter + id: PolylineAlgorithm_PolylineOptions_2_PolylineFormatter + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L68 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2.PolylineFormatter + commentId: P:PolylineAlgorithm.PolylineOptions`2.PolylineFormatter +- markdown: >- + Gets the formatter that converts between the raw character buffer and + + TPolyline. +- code: public IPolylineFormatter PolylineFormatter { get; } +- h4: Property Value +- parameters: + - type: + - text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html + - < + - TPolyline + - '>' +- api3: ValueFormatter + id: PolylineAlgorithm_PolylineOptions_2_ValueFormatter + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L62 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2.ValueFormatter + commentId: P:PolylineAlgorithm.PolylineOptions`2.ValueFormatter +- markdown: Gets the formatter that defines the column schema, scaling rules, and item factory. +- code: public IPolylineValueFormatter ValueFormatter { get; } +- h4: Property Value +- parameters: + - type: + - text: IPolylineValueFormatter + url: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.html + - < + - TValue + - '>' +languageId: csharp +metadata: + description: Provides unified configuration for a formatter-driven encoding or decoding operation. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineValueFormatter-1.yml b/api-reference/0.0/PolylineAlgorithm.PolylineValueFormatter-1.yml new file mode 100644 index 00000000..82de745a --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineValueFormatter-1.yml @@ -0,0 +1,226 @@ +### YamlMime:ApiPage +title: Class PolylineValueFormatter +body: +- api1: Class PolylineValueFormatter + id: PolylineAlgorithm_PolylineValueFormatter_1 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L29 + metadata: + uid: PolylineAlgorithm.PolylineValueFormatter`1 + commentId: T:PolylineAlgorithm.PolylineValueFormatter`1 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Provides an immutable, sealed rule engine that describes how to extract and scale values from + + an object of type T for polyline encoding, and how to reconstruct an + + object of type T from scaled values during decoding. +- code: 'public sealed class PolylineValueFormatter : IPolylineValueFormatter' +- h4: Type Parameters +- parameters: + - name: T + description: The source object type from which column values are extracted or to which they are reconstructed. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineValueFormatter + url: PolylineAlgorithm.PolylineValueFormatter-1.html +- h4: Implements +- list: + - text: IPolylineValueFormatter + url: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- +

+ + Instances of this class are constructed exclusively through . + +

+ +

+ + The sealed modifier allows the JIT to devirtualise and inline calls to + + and , eliminating vtable dispatch in the + + encoding/decoding hot loop. + +

+- h2: Properties +- api3: CanCreateItem + id: PolylineAlgorithm_PolylineValueFormatter_1_CanCreateItem + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L68 + metadata: + uid: PolylineAlgorithm.PolylineValueFormatter`1.CanCreateItem + commentId: P:PolylineAlgorithm.PolylineValueFormatter`1.CanCreateItem +- markdown: >- + Gets a value indicating whether a factory delegate was supplied at build time. + + When false, calling throws an + + . +- code: public bool CanCreateItem { get; } +- h4: Property Value +- parameters: + - type: + - text: bool + url: https://learn.microsoft.com/dotnet/api/system.boolean +- api3: HasBaselines + id: PolylineAlgorithm_PolylineValueFormatter_1_HasBaselines + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L61 + metadata: + uid: PolylineAlgorithm.PolylineValueFormatter`1.HasBaselines + commentId: P:PolylineAlgorithm.PolylineValueFormatter`1.HasBaselines +- markdown: >- + Gets a value indicating whether any column has a baseline defined. + + When false the encoder can skip the baseline-subtraction branch entirely, + + keeping the common-case encoding path branch-free. +- code: public bool HasBaselines { get; } +- h4: Property Value +- parameters: + - type: + - text: bool + url: https://learn.microsoft.com/dotnet/api/system.boolean +- api3: Width + id: PolylineAlgorithm_PolylineValueFormatter_1_Width + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L54 + metadata: + uid: PolylineAlgorithm.PolylineValueFormatter`1.Width + commentId: P:PolylineAlgorithm.PolylineValueFormatter`1.Width +- markdown: >- + Gets the number of columns (values per item). + + This is the required length of the passed to + + and the length of the span received by . +- code: public int Width { get; } +- h4: Property Value +- parameters: + - type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 +- h2: Methods +- api3: CreateItem(ReadOnlySpan) + id: PolylineAlgorithm_PolylineValueFormatter_1_CreateItem_System_ReadOnlySpan_System_Int64__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L108 + metadata: + uid: PolylineAlgorithm.PolylineValueFormatter`1.CreateItem(System.ReadOnlySpan{System.Int64}) + commentId: M:PolylineAlgorithm.PolylineValueFormatter`1.CreateItem(System.ReadOnlySpan{System.Int64}) +- markdown: Reconstructs a T from the given scaled integer values. +- code: public T CreateItem(ReadOnlySpan values) +- h4: Parameters +- parameters: + - name: values + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: The accumulated scaled integer values decoded from the polyline. +- h4: Returns +- parameters: + - type: + - T + description: A T reconstructed from values. +- h4: Exceptions +- parameters: + - type: + - text: InvalidOperationException + url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception + description: Thrown when no factory delegate was supplied via . +- api3: GetBaseline(int) + id: PolylineAlgorithm_PolylineValueFormatter_1_GetBaseline_System_Int32_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L128 + metadata: + uid: PolylineAlgorithm.PolylineValueFormatter`1.GetBaseline(System.Int32) + commentId: M:PolylineAlgorithm.PolylineValueFormatter`1.GetBaseline(System.Int32) +- markdown: >- + Returns the baseline for the column at index, or 0 if none is configured. + + The encoder subtracts this value from the first item's scaled column value during encoding. +- code: public long GetBaseline(int index) +- h4: Parameters +- parameters: + - name: index + type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + description: >- + The zero-based column index. Must be in the range [0, ). + + An is thrown if the index is out of range. +- h4: Returns +- parameters: + - type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: The baseline value, or 0 when no baseline has been defined for the column. +- api3: GetValues(T, Span) + id: PolylineAlgorithm_PolylineValueFormatter_1_GetValues__0_System_Span_System_Int64__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L83 + metadata: + uid: PolylineAlgorithm.PolylineValueFormatter`1.GetValues(`0,System.Span{System.Int64}) + commentId: M:PolylineAlgorithm.PolylineValueFormatter`1.GetValues(`0,System.Span{System.Int64}) +- markdown: >- + Extracts and scales all column values from item into the values span. + + Called once per item in the encoding hot loop. This method performs no heap allocation; + + the caller is responsible for providing and owning the output buffer. +- code: public void GetValues(T item, Span values) +- h4: Parameters +- parameters: + - name: item + type: + - T + description: The source item from which column values are extracted. + - name: values + type: + - text: Span + url: https://learn.microsoft.com/dotnet/api/system.span-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: >- + Output buffer that receives the scaled values. + + Its length must equal . +- h4: Exceptions +- parameters: + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when values.Length does not equal . +languageId: csharp +metadata: + description: >- + Provides an immutable, sealed rule engine that describes how to extract and scale values from + + an object of type T for polyline encoding, and how to reconstruct an + + object of type T from scaled values during decoding. diff --git a/api-reference/0.0/PolylineAlgorithm.yml b/api-reference/0.0/PolylineAlgorithm.yml index b60dc3c0..ad19be2d 100644 --- a/api-reference/0.0/PolylineAlgorithm.yml +++ b/api-reference/0.0/PolylineAlgorithm.yml @@ -16,6 +16,10 @@ body: url: PolylineAlgorithm.Extensions.html - h3: Classes - parameters: + - type: + text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-1.html + description: Provides a fluent builder for constructing a . - type: text: InvalidPolylineException url: PolylineAlgorithm.InvalidPolylineException.html @@ -35,4 +39,33 @@ body: text: PolylineEncodingOptionsBuilder url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html description: Provides a builder for configuring options for polyline encoding operations. + - type: + text: PolylineFormatter + url: PolylineAlgorithm.PolylineFormatter.html + description: >- + Provides static factory methods and ready-made instances of + + for the most common polyline surface types. + - type: + text: PolylineOptions + url: PolylineAlgorithm.PolylineOptions-2.html + description: Provides unified configuration for a formatter-driven encoding or decoding operation. + - type: + text: PolylineValueFormatter + url: PolylineAlgorithm.PolylineValueFormatter-1.html + description: >- + Provides an immutable, sealed rule engine that describes how to extract and scale values from + + an object of type T for polyline encoding, and how to reconstruct an + + object of type T from scaled values during decoding. +- h3: Delegates +- parameters: + - type: + text: PolylineItemFactory + url: PolylineAlgorithm.PolylineItemFactory-1.html + description: >- + Represents a factory method that reconstructs a T item from an array of + + scaled integer values decoded from a polyline. languageId: csharp diff --git a/api-reference/0.0/toc.yml b/api-reference/0.0/toc.yml index d1ffc58a..2004a401 100644 --- a/api-reference/0.0/toc.yml +++ b/api-reference/0.0/toc.yml @@ -3,6 +3,8 @@ href: PolylineAlgorithm.yml items: - name: Classes + - name: FormatterBuilder + href: PolylineAlgorithm.FormatterBuilder-1.yml - name: InvalidPolylineException href: PolylineAlgorithm.InvalidPolylineException.yml - name: PolylineEncoding @@ -11,6 +13,15 @@ href: PolylineAlgorithm.PolylineEncodingOptions.yml - name: PolylineEncodingOptionsBuilder href: PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml + - name: PolylineFormatter + href: PolylineAlgorithm.PolylineFormatter.yml + - name: PolylineOptions + href: PolylineAlgorithm.PolylineOptions-2.yml + - name: PolylineValueFormatter + href: PolylineAlgorithm.PolylineValueFormatter-1.yml + - name: Delegates + - name: PolylineItemFactory + href: PolylineAlgorithm.PolylineItemFactory-1.yml - name: PolylineAlgorithm.Abstraction href: PolylineAlgorithm.Abstraction.yml items: @@ -24,6 +35,10 @@ href: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml - name: IPolylineEncoder href: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml + - name: IPolylineFormatter + href: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml + - name: IPolylineValueFormatter + href: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml - name: PolylineAlgorithm.Extensions href: PolylineAlgorithm.Extensions.yml items: From 6e3a7c63201977b7d2932a227c4f1c6faa7a9739 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:26:16 +0000 Subject: [PATCH 08/22] feat: replace Abstract encoder/decoder with concrete PolylineEncoder/PolylineDecoder, unified IPolylineFormatter interface Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/3e9be87b-b3fb-4976-a90a-eef8d9eebf05 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../Abstraction/AbstractPolylineDecoder.cs | 292 ---------------- .../Abstraction/AbstractPolylineEncoder.cs | 320 ------------------ .../Abstraction/IPolylineFormatter.cs | 61 +++- .../Abstraction/IPolylineValueFormatter.cs | 63 ---- src/PolylineAlgorithm/FormatterBuilder.cs | 106 ++++-- .../Internal/DelegatePolylineFormatter.cs | 30 -- src/PolylineAlgorithm/PolylineDecoder.cs | 121 +++++++ src/PolylineAlgorithm/PolylineEncoder.cs | 144 ++++++++ .../PolylineEncodingOptions.cs | 83 ----- .../PolylineEncodingOptionsBuilder.cs | 102 ------ src/PolylineAlgorithm/PolylineFormatter.cs | 111 +++--- src/PolylineAlgorithm/PolylineOptions.cs | 74 ++-- .../PolylineValueFormatter.cs | 130 ------- src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 76 +++-- 14 files changed, 535 insertions(+), 1178 deletions(-) delete mode 100644 src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs delete mode 100644 src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs delete mode 100644 src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs delete mode 100644 src/PolylineAlgorithm/Internal/DelegatePolylineFormatter.cs create mode 100644 src/PolylineAlgorithm/PolylineDecoder.cs create mode 100644 src/PolylineAlgorithm/PolylineEncoder.cs delete mode 100644 src/PolylineAlgorithm/PolylineEncodingOptions.cs delete mode 100644 src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs delete mode 100644 src/PolylineAlgorithm/PolylineValueFormatter.cs diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs deleted file mode 100644 index 9dceb456..00000000 --- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs +++ /dev/null @@ -1,292 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -using Microsoft.Extensions.Logging; -using PolylineAlgorithm.Internal; -using PolylineAlgorithm.Internal.Diagnostics; -using System.Runtime.CompilerServices; - -namespace PolylineAlgorithm.Abstraction; - -/// -/// Provides a base implementation for decoding encoded polyline strings into sequences of geographic coordinates. -/// -/// -/// -/// Formatter-based use (no subclassing required): -/// Supply a via the -/// -/// constructor. The formatters handle all type-specific concerns; override nothing. -/// -/// -/// Legacy override-based use: -/// Derive from this class and override and -/// to provide type-specific behaviour. These overrides take priority over any registered formatter. -/// -/// -/// The type that represents the encoded polyline input. -/// The type that represents a decoded geographic coordinate. -public class AbstractPolylineDecoder : IPolylineDecoder { - private readonly ILogger> _logger; - private readonly IPolylineValueFormatter? _valueFormatter; - private readonly IPolylineFormatter? _polylineFormatter; - - /// - /// Initializes a new instance of the class with default encoding options. - /// - protected AbstractPolylineDecoder() - : this(new PolylineEncodingOptions()) { } - - /// - /// Initializes a new instance of the class with the specified encoding options. - /// - /// - /// The to use for encoding operations. - /// - /// - /// Thrown when is . - /// - protected AbstractPolylineDecoder(PolylineEncodingOptions options) { - if (options is null) { - ExceptionGuard.ThrowArgumentNull(nameof(options)); - } - - Options = options; - _logger = Options - .LoggerFactory - .CreateLogger>(); - } - - /// - /// Initializes a new instance of the class - /// using the supplied . - /// - /// - /// Use this constructor when you want formatter-driven decoding without subclassing. - /// The and hooks are not called; - /// all type-specific logic is delegated to the formatters. - /// - /// - /// A that carries both the value formatter and - /// the polyline formatter together with the underlying . - /// - /// - /// Thrown when is . - /// - public AbstractPolylineDecoder(PolylineOptions options) { - if (options is null) { - ExceptionGuard.ThrowArgumentNull(nameof(options)); - } - - Options = options.Encoding; - _polylineFormatter = options.PolylineFormatter; - _valueFormatter = options.ValueFormatter; - _logger = Options - .LoggerFactory - .CreateLogger>(); - } - - /// - /// Gets the encoding options used by this polyline decoder. - /// - public PolylineEncodingOptions Options { get; } - - /// - /// Decodes an encoded into a sequence of instances, - /// with support for cancellation. - /// - /// - /// The instance containing the encoded polyline string to decode. - /// - /// - /// A that can be used to cancel the decoding operation. - /// - /// - /// An of representing the decoded latitude and longitude pairs. - /// - /// - /// Thrown when is . - /// - /// - /// Thrown when is empty. - /// - /// - /// Thrown when the polyline format is invalid or malformed at a specific position. - /// - /// - /// Thrown when is canceled during decoding. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains two path implementations.")] - public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default) { - const string OperationName = nameof(Decode); - - _logger?.LogOperationStartedDebug(OperationName); - - ValidateNullPolyline(polyline, _logger); - - ReadOnlyMemory sequence = (_valueFormatter is not null && _polylineFormatter is not null) - ? _polylineFormatter.Read(polyline) - : GetReadOnlyMemory(in polyline); - - ValidateSequence(sequence, _logger); - ValidateFormat(sequence, _logger); - - int position = 0; - - if (_valueFormatter is not null && _polylineFormatter is not null) { - int width = _valueFormatter.Width; - int[] accumulated = new int[width]; - long[] longValues = new long[width]; - - try { - while (position < sequence.Length) { - cancellationToken.ThrowIfCancellationRequested(); - - for (int j = 0; j < width; j++) { - if (!PolylineEncoding.TryReadValue(ref accumulated[j], sequence, ref position)) { - _logger?.LogOperationFailedDebug(OperationName); - _logger?.LogInvalidPolylineWarning(position); - ExceptionGuard.ThrowInvalidPolylineFormat(position); - } - } - - for (int j = 0; j < width; j++) { - longValues[j] = accumulated[j]; - } - - yield return _valueFormatter.CreateItem(longValues.AsSpan()); - } - } finally { - _logger?.LogOperationFinishedDebug(OperationName); - } - } else { - int encodedLatitude = 0; - int encodedLongitude = 0; - - try { - while (position < sequence.Length) { - cancellationToken.ThrowIfCancellationRequested(); - - if (!PolylineEncoding.TryReadValue(ref encodedLatitude, sequence, ref position) - || !PolylineEncoding.TryReadValue(ref encodedLongitude, sequence, ref position)) { - _logger?.LogOperationFailedDebug(OperationName); - _logger?.LogInvalidPolylineWarning(position); - - ExceptionGuard.ThrowInvalidPolylineFormat(position); - } - - double decodedLatitude = PolylineEncoding.Denormalize(encodedLatitude, Options.Precision); - double decodedLongitude = PolylineEncoding.Denormalize(encodedLongitude, Options.Precision); - - _logger?.LogDecodedCoordinateDebug(decodedLatitude, decodedLongitude, position); - - yield return CreateCoordinate(decodedLatitude, decodedLongitude); - } - } finally { - _logger?.LogOperationFinishedDebug(OperationName); - } - } - } - - /// - /// Validates that the provided polyline is not . - /// - /// The polyline instance to validate. - /// An optional used to log a warning when validation fails. - /// - /// Thrown when is . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ValidateNullPolyline(TPolyline polyline, ILogger? logger) { - if (polyline is null) { - logger?.LogNullArgumentWarning(nameof(polyline)); - ExceptionGuard.ThrowArgumentNull(nameof(polyline)); - } - } - - /// - /// Validates that the polyline character sequence meets the minimum required length. - /// - /// The polyline character sequence to validate. - /// An optional used to log diagnostic messages when validation fails. - /// - /// Thrown when is shorter than the minimum allowed length. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ValidateSequence(ReadOnlyMemory polylineSequence, ILogger? logger) { - if (polylineSequence.Length < Defaults.Polyline.Block.Length.Min) { - logger?.LogOperationFailedDebug(nameof(Decode)); - logger?.LogPolylineCannotBeShorterThanWarning(polylineSequence.Length, Defaults.Polyline.Block.Length.Min); - - ExceptionGuard.ThrowInvalidPolylineLength(polylineSequence.Length, Defaults.Polyline.Block.Length.Min); - } - } - - /// - /// Validates the format of the polyline character sequence, ensuring all characters are within the allowed range. - /// - /// - /// The read-only memory region of characters representing the polyline to validate. - /// - /// - /// An optional used to log a warning when format validation fails. - /// - /// - /// Thrown when the polyline contains characters outside the valid encoding range or has an invalid block structure. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual void ValidateFormat(ReadOnlyMemory sequence, ILogger? logger) { - try { - PolylineEncoding.ValidateFormat(sequence.Span); - } catch (ArgumentException ex) { - logger?.LogInvalidPolylineFormatWarning(ex); - - throw; - } - } - - /// - /// Extracts the underlying read-only memory region of characters from the specified polyline instance. - /// - /// - /// The instance from which to extract the character sequence. - /// - /// - /// A of representing the encoded polyline characters. - /// - /// - /// Thrown by the default implementation when no polyline formatter is registered and the method - /// has not been overridden in a derived class. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline) { - throw new NotSupportedException( - $"Override {nameof(GetReadOnlyMemory)} in a derived class, or provide a " + - $"{nameof(PolylineOptions)} with a polyline formatter."); - } - - /// - /// Creates a instance from the specified latitude and longitude values. - /// - /// - /// The latitude component of the coordinate, in degrees. - /// - /// - /// The longitude component of the coordinate, in degrees. - /// - /// - /// A instance representing the specified geographic coordinate. - /// - /// - /// Thrown by the default implementation when no value formatter is registered and the method - /// has not been overridden in a derived class. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual TCoordinate CreateCoordinate(double latitude, double longitude) { - throw new NotSupportedException( - $"Override {nameof(CreateCoordinate)} in a derived class, or provide a " + - $"{nameof(PolylineOptions)} with a value formatter."); - } -} diff --git a/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs b/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs deleted file mode 100644 index 8873d939..00000000 --- a/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs +++ /dev/null @@ -1,320 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.Abstraction; - -using Microsoft.Extensions.Logging; -using PolylineAlgorithm; -using PolylineAlgorithm.Internal; -using PolylineAlgorithm.Internal.Diagnostics; -using System; -using System.Buffers; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; - -/// -/// Provides a base implementation for encoding sequences of geographic coordinates into encoded polyline strings. -/// -/// -/// -/// Formatter-based use (no subclassing required): -/// Supply a via the -/// -/// constructor. The formatter handles all type-specific concerns; override nothing. -/// -/// -/// Legacy override-based use: -/// Derive from this class and override , , -/// and to provide type-specific behaviour. These overrides take -/// priority over any registered formatter. -/// -/// -/// The type that represents a geographic coordinate to encode. -/// The type that represents the encoded polyline output. -public class AbstractPolylineEncoder : IPolylineEncoder { - private readonly ILogger> _logger; - private readonly IPolylineValueFormatter? _valueFormatter; - private readonly IPolylineFormatter? _polylineFormatter; - - /// - /// Initializes a new instance of the class with default encoding options. - /// - protected AbstractPolylineEncoder() - : this(new PolylineEncodingOptions()) { } - - /// - /// Initializes a new instance of the class with the specified encoding options. - /// - /// - /// The to use for encoding operations. - /// - /// Thrown when is - protected AbstractPolylineEncoder(PolylineEncodingOptions options) { - if (options is null) { - ExceptionGuard.ThrowArgumentNull(nameof(options)); - } - - Options = options; - _logger = Options - .LoggerFactory - .CreateLogger>(); - } - - /// - /// Initializes a new instance of the class - /// using the supplied . - /// - /// - /// Use this constructor when you want formatter-driven encoding without subclassing. - /// The , , and hooks - /// are not called; all type-specific logic is delegated to the formatters. - /// - /// - /// A that carries both the value formatter and - /// the polyline formatter together with the underlying . - /// - /// - /// Thrown when is . - /// - public AbstractPolylineEncoder(PolylineOptions options) { - if (options is null) { - ExceptionGuard.ThrowArgumentNull(nameof(options)); - } - - Options = options.Encoding; - _valueFormatter = options.ValueFormatter; - _polylineFormatter = options.PolylineFormatter; - _logger = Options - .LoggerFactory - .CreateLogger>(); - } - - /// - /// Gets the encoding options used by this polyline encoder. - /// - public PolylineEncodingOptions Options { get; } - - /// - /// Encodes a collection of instances into an encoded string. - /// - /// - /// The collection of objects to encode. - /// - /// - /// A that can be used to cancel the encoding operation. - /// - /// - /// An instance of representing the encoded coordinates. - /// - /// - /// Thrown when is . - /// - /// - /// Thrown when is an empty enumeration. - /// - /// - /// Thrown when the internal encoding buffer cannot accommodate the encoded value. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains local methods. Actual method only 55 lines.")] - public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) { - const string OperationName = nameof(Encode); - - _logger - .LogOperationStartedDebug(OperationName); - - Debug.Assert(coordinates.Length >= 0, "Count must be non-negative."); - - ValidateEmptyCoordinates(ref coordinates, _logger); - - if (_valueFormatter is not null && _polylineFormatter is not null) { - return EncodeWithFormatter(coordinates, cancellationToken); - } - - CoordinateDelta delta = new(); - - int position = 0; - int consumed = 0; - int length = GetMaxBufferLength(coordinates.Length, 2); - - char[]? temp = length <= Options.StackAllocLimit - ? null - : ArrayPool.Shared.Rent(length); - - Span buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length); - - string encodedResult; - - try { - for (var i = 0; i < coordinates.Length; i++) { - cancellationToken.ThrowIfCancellationRequested(); - - delta - .Next( - PolylineEncoding.Normalize(GetLatitude(coordinates[i]), Options.Precision), - PolylineEncoding.Normalize(GetLongitude(coordinates[i]), Options.Precision) - ); - - if (!PolylineEncoding.TryWriteValue(delta.Latitude, buffer, ref position) - || !PolylineEncoding.TryWriteValue(delta.Longitude, buffer, ref position) - ) { - // This shouldn't happen, but if it does, log the error and throw an exception. - _logger - .LogOperationFailedDebug(OperationName); - _logger - .LogCannotWriteValueToBufferWarning(position, consumed); - - ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer(); - - } - - consumed++; - } - - encodedResult = buffer[..position].ToString(); - } finally { - if (temp is not null) { - ArrayPool.Shared.Return(temp); - } - } - - _logger - .LogOperationFinishedDebug(OperationName); - - return CreatePolyline(encodedResult.AsMemory()); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static void ValidateEmptyCoordinates(ref ReadOnlySpan coordinates, ILogger logger) { - if (coordinates.Length < 1) { - logger - .LogOperationFailedDebug(OperationName); - logger - .LogEmptyArgumentWarning(nameof(coordinates)); - - ExceptionGuard.ThrowArgumentCannotBeEmptyEnumerationMessage(nameof(coordinates)); - } - } - } - - /// - /// Encodes coordinates using the registered value and polyline formatters. - /// - private TPolyline EncodeWithFormatter(ReadOnlySpan coordinates, CancellationToken cancellationToken) { - const string OperationName = nameof(Encode); - int width = _valueFormatter!.Width; - int length = GetMaxBufferLength(coordinates.Length, width); - - char[]? temp = length <= Options.StackAllocLimit - ? null - : ArrayPool.Shared.Rent(length); - - Span buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length); - - int position = 0; - long[] previous = new long[width]; - long[] values = new long[width]; - string encodedResult; - - for (int j = 0; j < width; j++) { - previous[j] = _valueFormatter.GetBaseline(j); - } - - try { - for (var i = 0; i < coordinates.Length; i++) { - cancellationToken.ThrowIfCancellationRequested(); - - _valueFormatter.GetValues(coordinates[i], values.AsSpan()); - - for (int j = 0; j < width; j++) { - long current = values[j]; - long delta = current - previous[j]; - previous[j] = current; - - if (!PolylineEncoding.TryWriteValue((int)delta, buffer, ref position)) { - _logger.LogOperationFailedDebug(OperationName); - _logger.LogCannotWriteValueToBufferWarning(position, i); - ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer(); - } - } - } - - encodedResult = buffer[..position].ToString(); - } finally { - if (temp is not null) { - ArrayPool.Shared.Return(temp); - } - } - - _logger.LogOperationFinishedDebug(OperationName); - - return _polylineFormatter!.Write(encodedResult.AsMemory()); - } - - /// - /// Creates a polyline instance from the provided read-only sequence of characters. - /// - /// A containing the encoded polyline characters. - /// - /// An instance of representing the encoded polyline. - /// - /// - /// Thrown by the default implementation when no polyline formatter is registered and the method - /// has not been overridden in a derived class. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual TPolyline CreatePolyline(ReadOnlyMemory polyline) { - throw new NotSupportedException( - $"Override {nameof(CreatePolyline)} in a derived class, or provide a " + - $"{nameof(PolylineOptions)} with a polyline formatter."); - } - - /// - /// Extracts the longitude value from the specified coordinate. - /// - /// The coordinate from which to extract the longitude. - /// - /// The longitude value as a . - /// - /// - /// Thrown by the default implementation when no value formatter is registered and the method - /// has not been overridden in a derived class. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual double GetLongitude(TCoordinate current) { - throw new NotSupportedException( - $"Override {nameof(GetLatitude)} and {nameof(GetLongitude)} in a derived class, or " + - $"provide a {nameof(PolylineOptions)} with a value formatter."); - } - - /// - /// Extracts the latitude value from the specified coordinate. - /// - /// The coordinate from which to extract the latitude. - /// - /// The latitude value as a . - /// - /// - /// Thrown by the default implementation when no value formatter is registered and the method - /// has not been overridden in a derived class. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual double GetLatitude(TCoordinate current) { - throw new NotSupportedException( - $"Override {nameof(GetLatitude)} and {nameof(GetLongitude)} in a derived class, or " + - $"provide a {nameof(PolylineOptions)} with a value formatter."); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int GetMaxBufferLength(int count, int valuesPerItem) { - Debug.Assert(count > 0, "Count must be greater than zero."); - Debug.Assert(valuesPerItem > 0, "Values per item must be greater than zero."); - - int requestedBufferLength = count * valuesPerItem * Defaults.Polyline.Block.Length.Max; - - Debug.Assert(requestedBufferLength > 0, "Requested buffer length must be greater than zero."); - - return requestedBufferLength; - } -} diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs index c7ba59e4..09c52504 100644 --- a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs +++ b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs @@ -8,28 +8,48 @@ namespace PolylineAlgorithm.Abstraction; using System; /// -/// Defines how to produce a from an encoded character buffer (output/write -/// direction), and how to extract that buffer back from a (input/read -/// direction). +/// Defines how to extract and scale values from a for encoding, +/// reconstruct a from scaled values for decoding, +/// produce a from an encoded character buffer, and extract that buffer +/// back from a . /// -/// The polyline surface type — for example or +/// The coordinate or item type. For example a struct with Latitude/Longitude. +/// The polyline surface type. For example or /// of . /// -/// -/// This interface is the polyline-surface counterpart to . -/// The engine exclusively works with of internally. -/// The formatter is the only code that touches . -/// -/// -/// Use , , or -/// to obtain a ready-made implementation. -/// +/// Use to build a +/// that implements this interface. /// -public interface IPolylineFormatter { +public interface IPolylineFormatter { + /// + /// Gets the number of values (columns) per encoded item. + /// This is the required length of the passed to + /// and the length of the span received in . + /// + int Width { get; } + + /// + /// Returns the baseline (epoch) for the column at , or 0 if none is configured. + /// The encoder subtracts this value from the first item's scaled column value to keep the initial delta small. + /// + /// The zero-based column index. Must be in the range [0, ). + /// The baseline value, or 0 when no baseline has been defined for the column. + long GetBaseline(int index) => 0L; + + /// + /// Extracts and scales all column values from into the span. + /// Called once per item in the encoding loop. + /// + /// The source item from which column values are extracted. + /// + /// Output buffer that receives the scaled integer values. Its length must equal . + /// + void GetValues(TCoordinate item, Span values); + /// /// Creates a from the encoded character buffer produced by the encoder. /// - /// The encoded polyline as a read-only span of characters. + /// The encoded polyline as a read-only memory of characters. /// A wrapping or derived from . TPolyline Write(ReadOnlyMemory encoded); @@ -39,4 +59,15 @@ public interface IPolylineFormatter { /// The polyline to read from. /// A of representing the encoded characters. ReadOnlyMemory Read(TPolyline polyline); + + /// + /// Reconstructs a from the given scaled integer values. + /// Called once per decoded item in the decoding loop. + /// + /// + /// The accumulated scaled integer values decoded from the polyline. Each element corresponds to + /// the same column position as in . + /// + /// A reconstructed from . + TCoordinate CreateItem(ReadOnlySpan values); } diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs b/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs deleted file mode 100644 index c6c1a204..00000000 --- a/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.Abstraction; - -using System; - -/// -/// Defines how to extract scaled numeric values from a during encoding, and -/// how to reconstruct a from those values during decoding. -/// -/// The coordinate or item type that the formatter understands. -/// -/// -/// This interface is the coordinate-side counterpart to . -/// Together they allow the engine base classes to be used directly — without subclassing — by supplying -/// both formatters via . -/// -/// -/// Use to build a that -/// already implements this interface. -/// -/// -public interface IPolylineValueFormatter { - /// - /// Extracts and scales all column values from into the span. - /// Called once per item in the encoding loop. - /// - /// The source item from which column values are extracted. - /// - /// Output buffer that receives the scaled integer values. Its length must equal the number of columns - /// defined by this formatter. - /// - void GetValues(TValue item, Span values); - - /// - /// Reconstructs a from the given scaled integer values. - /// Called once per decoded item in the decoding loop. - /// - /// - /// The accumulated scaled integer values decoded from the polyline. Each element corresponds to - /// the same column position as in . - /// - /// A reconstructed from . - TValue CreateItem(ReadOnlySpan values); - - /// - /// Gets the number of values (columns) per encoded item. - /// This is the required length of the buffer passed to and - /// the length of the span received in . - /// - int Width { get; } - - /// - /// Returns the baseline (epoch) for the column at , or 0 if none is configured. - /// The encoder subtracts this value from the first item's scaled column value to keep the initial delta small. - /// - /// The zero-based column index. Must be in the range [0, ). - /// The baseline value, or 0 when no baseline has been defined for the column. - long GetBaseline(int index) => 0L; -} diff --git a/src/PolylineAlgorithm/FormatterBuilder.cs b/src/PolylineAlgorithm/FormatterBuilder.cs index 9a491c02..18edef89 100644 --- a/src/PolylineAlgorithm/FormatterBuilder.cs +++ b/src/PolylineAlgorithm/FormatterBuilder.cs @@ -10,33 +10,37 @@ namespace PolylineAlgorithm; using System.Collections.Generic; /// -/// Provides a fluent builder for constructing a . +/// Provides a fluent builder for constructing a . /// -/// The source object type from which column values are extracted. +/// The coordinate or item type from which column values are extracted. +/// The polyline surface type produced and consumed by the formatter. /// /// /// Use to obtain an instance, call once per column, /// optionally chain to specify an epoch for the most-recently added column, /// optionally chain to register a factory for the decoding direction, -/// then call to produce the immutable . +/// call to supply the polyline surface delegates (required), then call +/// to produce the immutable . /// /// -/// The builder is the only way to create a — its -/// constructor is internal. +/// The builder is the only way to create a +/// — its constructor is internal. /// /// -public sealed class FormatterBuilder { - private readonly List> _rules = []; +public sealed class FormatterBuilder { + private readonly List> _rules = []; private readonly HashSet _names = new(StringComparer.Ordinal); - private PolylineItemFactory? _create; + private PolylineItemFactory? _create; + private Func, TPolyline>? _write; + private Func>? _read; private FormatterBuilder() { } /// - /// Creates a new instance. + /// Creates a new instance. /// - /// A fresh with no rules. - public static FormatterBuilder Create() => new(); + /// A fresh builder with no rules and no polyline delegates. + public static FormatterBuilder Create() => new(); /// /// Adds a column with the specified value selector and precision. @@ -46,20 +50,20 @@ private FormatterBuilder() { } /// /// /// A delegate that extracts the column's raw value from an item of type - /// . + /// . /// /// /// The number of decimal places to preserve. Each extracted value is multiplied by /// 10^ before encoding. Defaults to 5. /// - /// The current instance for method chaining. + /// The current builder instance for method chaining. /// /// Thrown when or is . /// /// /// Thrown when is empty, or a rule with the same name already exists. /// - public FormatterBuilder AddValue(string name, Func selector, uint precision = 5) { + public FormatterBuilder AddValue(string name, Func selector, uint precision = 5) { if (name is null) { throw new ArgumentNullException(nameof(name)); } @@ -76,7 +80,7 @@ public FormatterBuilder AddValue(string name, Func selector, uint throw new ArgumentException($"A rule with the name '{name}' has already been added.", nameof(name)); } - _rules.Add(new FormatterRule(name, (long)Pow10.GetFactor(precision), selector)); + _rules.Add(new FormatterRule(name, (long)Pow10.GetFactor(precision), selector)); return this; } @@ -87,35 +91,36 @@ public FormatterBuilder AddValue(string name, Func selector, uint /// keeping the initial delta small when the absolute first value is large. /// /// The baseline value to apply to the first item's column value. - /// The current instance for method chaining. + /// The current builder instance for method chaining. /// - /// Thrown when no rules have been added yet. Call before . + /// Thrown when no rules have been added yet. Call before + /// . /// - public FormatterBuilder SetBaseline(long baseline) { + public FormatterBuilder SetBaseline(long baseline) { if (_rules.Count == 0) { throw new InvalidOperationException("Cannot set a baseline when no rules have been added. Call AddValue first."); } var last = _rules[^1]; - _rules[^1] = new FormatterRule(last.Name, last.Factor, last.Select, baseline); + _rules[^1] = new FormatterRule(last.Name, last.Factor, last.Select, baseline); return this; } /// - /// Registers a factory delegate used to reconstruct a from scaled values - /// during decoding. This enables the decoding direction of . + /// Registers a factory delegate used to reconstruct a from + /// scaled values during decoding. /// /// /// A delegate that accepts the scaled integer values decoded from the polyline and returns a - /// . The span length always equals the number of columns added via - /// . + /// . The span length always equals the number of columns added + /// via . /// - /// The current instance for method chaining. + /// The current builder instance for method chaining. /// /// Thrown when is . /// - public FormatterBuilder WithCreate(PolylineItemFactory create) { + public FormatterBuilder WithCreate(PolylineItemFactory create) { if (create is null) { throw new ArgumentNullException(nameof(create)); } @@ -126,19 +131,60 @@ public FormatterBuilder WithCreate(PolylineItemFactory create) { } /// - /// Bakes all added rules into a sealed, immutable . + /// Supplies the polyline-surface delegates required to convert between the raw character buffer + /// and a . This call is mandatory before . + /// + /// + /// Converts the encoded of produced by the encoder + /// into a . + /// + /// + /// Extracts the encoded character buffer from a for the decoder to + /// consume. + /// + /// The current builder instance for method chaining. + /// + /// Thrown when or is . + /// + public FormatterBuilder ForPolyline( + Func, TPolyline> write, + Func> read) { + if (write is null) { + throw new ArgumentNullException(nameof(write)); + } + + if (read is null) { + throw new ArgumentNullException(nameof(read)); + } + + _write = write; + _read = read; + + return this; + } + + /// + /// Bakes all added rules and delegates into a sealed, immutable + /// . /// /// - /// An immutable whose rules can no longer be changed. + /// An immutable whose configuration can + /// no longer be changed. /// /// - /// Thrown when no rules have been added. + /// Thrown when no rules have been added, or when has not been called. /// - public PolylineValueFormatter Build() { + public PolylineFormatter Build() { if (_rules.Count == 0) { throw new InvalidOperationException("At least one rule must be added before calling Build."); } - return new PolylineValueFormatter(_rules.ToArray(), _create); + if (_write is null || _read is null) { + throw new InvalidOperationException( + $"Polyline surface delegates must be supplied before calling Build. " + + $"Call {nameof(ForPolyline)} first."); + } + + return new PolylineFormatter(_rules.ToArray(), _create, _write, _read); } } diff --git a/src/PolylineAlgorithm/Internal/DelegatePolylineFormatter.cs b/src/PolylineAlgorithm/Internal/DelegatePolylineFormatter.cs deleted file mode 100644 index 82d44b39..00000000 --- a/src/PolylineAlgorithm/Internal/DelegatePolylineFormatter.cs +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.Internal; - -using PolylineAlgorithm.Abstraction; -using System; - -/// -/// A sealed delegate-backed implementation of . -/// Produced by and the built-in factory properties. -/// -/// The polyline surface type. -internal sealed class DelegatePolylineFormatter : IPolylineFormatter { - private readonly Func, T> _write; - private readonly Func> _read; - - internal DelegatePolylineFormatter(Func, T> write, Func> read) { - _write = write; - _read = read; - } - - /// - public T Write(ReadOnlyMemory encoded) => _write(encoded); - - /// - public ReadOnlyMemory Read(T polyline) => _read(polyline); -} diff --git a/src/PolylineAlgorithm/PolylineDecoder.cs b/src/PolylineAlgorithm/PolylineDecoder.cs new file mode 100644 index 00000000..df02e0e7 --- /dev/null +++ b/src/PolylineAlgorithm/PolylineDecoder.cs @@ -0,0 +1,121 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +using Microsoft.Extensions.Logging; +using PolylineAlgorithm.Abstraction; +using PolylineAlgorithm.Internal; +using PolylineAlgorithm.Internal.Diagnostics; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; + +/// +/// Decodes encoded polyline representations into sequences of geographic coordinates. +/// +/// The type that represents the encoded polyline input. +/// The type that represents a decoded geographic coordinate. +/// +/// Pass a that carries a +/// to the constructor. The formatter handles +/// all type-specific concerns; no subclassing is required. +/// +public class PolylineDecoder : IPolylineDecoder { + private readonly IPolylineFormatter _formatter; + private readonly ILogger> _logger; + + /// + /// Initializes a new instance of . + /// + /// + /// A that carries the formatter and settings. + /// Must not be . + /// + /// + /// Thrown when is . + /// + public PolylineDecoder(PolylineOptions options) { + if (options is null) { + ExceptionGuard.ThrowArgumentNull(nameof(options)); + } + + _formatter = options.Formatter; + _logger = options.LoggerFactory.CreateLogger>(); + } + + /// + /// Decodes an encoded into a sequence of + /// instances. + /// + /// The encoded polyline to decode. Must not be . + /// A token that can be used to cancel the operation. + /// + /// An of representing the decoded + /// coordinates in the original order. + /// + /// + /// Thrown when is . + /// + /// + /// Thrown when the polyline format is invalid or malformed. + /// + /// + /// Thrown when is canceled during decoding. + /// + public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default) { + const string OperationName = nameof(Decode); + + _logger.LogOperationStartedDebug(OperationName); + + if (polyline is null) { + _logger.LogNullArgumentWarning(nameof(polyline)); + ExceptionGuard.ThrowArgumentNull(nameof(polyline)); + } + + ReadOnlyMemory sequence = _formatter.Read(polyline); + + if (sequence.Length < Defaults.Polyline.Block.Length.Min) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogPolylineCannotBeShorterThanWarning(sequence.Length, Defaults.Polyline.Block.Length.Min); + ExceptionGuard.ThrowInvalidPolylineLength(sequence.Length, Defaults.Polyline.Block.Length.Min); + } + + try { + PolylineEncoding.ValidateFormat(sequence.Span); + } catch (ArgumentException ex) { + _logger.LogInvalidPolylineFormatWarning(ex); + throw; + } + + int width = _formatter.Width; + int[] accumulated = new int[width]; + long[] longValues = new long[width]; + int position = 0; + + try { + while (position < sequence.Length) { + cancellationToken.ThrowIfCancellationRequested(); + + for (int j = 0; j < width; j++) { + if (!PolylineEncoding.TryReadValue(ref accumulated[j], sequence, ref position)) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogInvalidPolylineWarning(position); + ExceptionGuard.ThrowInvalidPolylineFormat(position); + } + } + + for (int j = 0; j < width; j++) { + longValues[j] = accumulated[j]; + } + + yield return _formatter.CreateItem(longValues.AsSpan()); + } + } finally { + _logger.LogOperationFinishedDebug(OperationName); + } + } +} diff --git a/src/PolylineAlgorithm/PolylineEncoder.cs b/src/PolylineAlgorithm/PolylineEncoder.cs new file mode 100644 index 00000000..a42b942a --- /dev/null +++ b/src/PolylineAlgorithm/PolylineEncoder.cs @@ -0,0 +1,144 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +using Microsoft.Extensions.Logging; +using PolylineAlgorithm.Abstraction; +using PolylineAlgorithm.Internal; +using PolylineAlgorithm.Internal.Diagnostics; +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +/// +/// Encodes sequences of geographic coordinates into encoded polyline representations. +/// +/// The type that represents a geographic coordinate to encode. +/// The type that represents the encoded polyline output. +/// +/// Pass a that carries a +/// to the constructor. The formatter handles +/// all type-specific concerns; no subclassing is required. +/// +public class PolylineEncoder : IPolylineEncoder { + private readonly IPolylineFormatter _formatter; + private readonly PolylineOptions _options; + private readonly ILogger> _logger; + + /// + /// Initializes a new instance of . + /// + /// + /// A that carries the formatter and settings. + /// Must not be . + /// + /// + /// Thrown when is . + /// + public PolylineEncoder(PolylineOptions options) { + if (options is null) { + ExceptionGuard.ThrowArgumentNull(nameof(options)); + } + + _options = options; + _formatter = options.Formatter; + _logger = options.LoggerFactory.CreateLogger>(); + } + + /// + /// Encodes a collection of instances into an encoded + /// . + /// + /// The collection of coordinates to encode. + /// A token that can be used to cancel the operation. + /// + /// An instance of representing the encoded coordinates. + /// + /// + /// Thrown when is empty. + /// + /// + /// Thrown when the internal encoding buffer cannot accommodate the encoded value. + /// + /// + /// Thrown when is canceled. + /// + public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) { + const string OperationName = nameof(Encode); + + _logger.LogOperationStartedDebug(OperationName); + + Debug.Assert(coordinates.Length >= 0, "Count must be non-negative."); + + if (coordinates.Length < 1) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogEmptyArgumentWarning(nameof(coordinates)); + ExceptionGuard.ThrowArgumentCannotBeEmptyEnumerationMessage(nameof(coordinates)); + } + + int width = _formatter.Width; + int length = GetMaxBufferLength(coordinates.Length, width); + + char[]? temp = length <= _options.StackAllocLimit + ? null + : ArrayPool.Shared.Rent(length); + + Span buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length); + + int position = 0; + long[] previous = new long[width]; + long[] values = new long[width]; + + for (int j = 0; j < width; j++) { + previous[j] = _formatter.GetBaseline(j); + } + + try { + for (int i = 0; i < coordinates.Length; i++) { + cancellationToken.ThrowIfCancellationRequested(); + + _formatter.GetValues(coordinates[i], values.AsSpan()); + + for (int j = 0; j < width; j++) { + long current = values[j]; + long delta = current - previous[j]; + previous[j] = current; + + if (!PolylineEncoding.TryWriteValue((int)delta, buffer, ref position)) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogCannotWriteValueToBufferWarning(position, i); + ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer(); + } + } + } + + // Convert to string inside the try block so the buffer is still valid. + string encodedResult = buffer[..position].ToString(); + + _logger.LogOperationFinishedDebug(OperationName); + + return _formatter.Write(encodedResult.AsMemory()); + } finally { + if (temp is not null) { + ArrayPool.Shared.Return(temp); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetMaxBufferLength(int count, int valuesPerItem) { + Debug.Assert(count > 0, "Count must be greater than zero."); + Debug.Assert(valuesPerItem > 0, "Values per item must be greater than zero."); + + int requestedBufferLength = count * valuesPerItem * Defaults.Polyline.Block.Length.Max; + + Debug.Assert(requestedBufferLength > 0, "Requested buffer length must be greater than zero."); + + return requestedBufferLength; + } +} diff --git a/src/PolylineAlgorithm/PolylineEncodingOptions.cs b/src/PolylineAlgorithm/PolylineEncodingOptions.cs deleted file mode 100644 index e99843de..00000000 --- a/src/PolylineAlgorithm/PolylineEncodingOptions.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm; - -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -/// -/// Provides configuration options for polyline encoding operations. -/// -/// -/// -/// This class allows you to configure various aspects of polyline encoding, including: -/// -/// -/// The level for coordinate encoding -/// The for memory allocation strategy -/// The for diagnostic logging -/// -/// -/// All properties have internal setters and should be configured through a builder or factory pattern. -/// -/// -[DebuggerDisplay("StackAllocLimit: {StackAllocLimit}, Precision: {Precision}, LoggerFactoryType: {GetLoggerFactoryType()}")] -public sealed class PolylineEncodingOptions { - /// - /// Gets the logger factory used for diagnostic logging during encoding operations. - /// - /// - /// An instance. Defaults to . - /// - /// - /// The default logger factory is , which does not log any messages. - /// To enable logging, provide a custom implementation. - /// - public ILoggerFactory LoggerFactory { get; internal set; } = NullLoggerFactory.Instance; - - /// - /// Gets the precision level used for encoding coordinate values. - /// - /// - /// The number of decimal places to use when encoding coordinate values. Defaults to 5. - /// - /// - /// - /// The precision determines the number of decimal places to which each coordinate value (latitude or longitude) - /// is multiplied and truncated (not rounded) before encoding. For example, a precision of 5 means each coordinate is multiplied by 10^5 - /// and truncated to an integer before encoding. - /// - /// - /// This setting does not directly correspond to a physical distance or accuracy in meters, but rather controls - /// the granularity of the encoded values. - /// - /// - public uint Precision { get; internal set; } = 5; - - /// - /// Gets the maximum buffer size (in characters) that can be allocated on the stack for encoding operations. - /// - /// - /// The maximum number of characters for stack allocation using stackalloc char[]. Defaults to 512. - /// - /// - /// When the required buffer size for encoding exceeds this limit, memory will be allocated on the heap instead of the stack. - /// This setting specifically applies to stack allocation of character arrays (stackalloc char[]) used during polyline encoding, - /// balancing performance and stack safety. - /// - public int StackAllocLimit { get; internal set; } = 512; - - /// - /// Returns the type name of the logger factory for debugging purposes. - /// - /// - /// A string containing the type name of the current instance. - /// - [ExcludeFromCodeCoverage] - private string GetLoggerFactoryType() => LoggerFactory.GetType().Name; -} \ No newline at end of file diff --git a/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs b/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs deleted file mode 100644 index 4258beca..00000000 --- a/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs +++ /dev/null @@ -1,102 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm; - -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using PolylineAlgorithm.Internal.Diagnostics; - -/// -/// Provides a builder for configuring options for polyline encoding operations. -/// -public sealed class PolylineEncodingOptionsBuilder { - private uint _precision = 5; - private int _stackAllocLimit = 512; - private ILoggerFactory _loggerFactory = NullLoggerFactory.Instance; - - private PolylineEncodingOptionsBuilder() { } - - /// - /// Creates a new instance for the specified coordinate type. - /// - /// - /// An instance for configuring polyline encoding options. - /// - public static PolylineEncodingOptionsBuilder Create() { - return new PolylineEncodingOptionsBuilder(); - } - - /// - /// Builds a new instance using the configured options. - /// - /// - /// A configured instance. - /// - public PolylineEncodingOptions Build() { - return new PolylineEncodingOptions { - Precision = _precision, - StackAllocLimit = _stackAllocLimit, - LoggerFactory = _loggerFactory, - }; - } - - /// - /// Configures the buffer size used for stack allocation during polyline encoding operations. - /// - /// - /// The maximum buffer size to use for stack allocation. Must be greater than or equal to 1. - /// - /// - /// The current instance for method chaining. - /// - /// - /// Thrown if is less than 1. - /// - /// - /// This method allows customization of the internal buffer size for encoding, which can impact performance and memory usage. - /// - public PolylineEncodingOptionsBuilder WithStackAllocLimit(int stackAllocLimit) { - const int minStackAllocLimit = 1; - - if (minStackAllocLimit > stackAllocLimit) { - ExceptionGuard.StackAllocLimitMustBeEqualOrGreaterThan(minStackAllocLimit, nameof(stackAllocLimit)); - } - - _stackAllocLimit = stackAllocLimit; - - return this; - } - - /// - /// Sets the coordinate encoding precision. - /// - /// - /// The number of decimal places to use for encoding coordinate values. Default is 5. - /// - /// - /// The current instance for method chaining. - /// - public PolylineEncodingOptionsBuilder WithPrecision(uint precision) { - _precision = precision; - - return this; - } - - /// - /// Configures the to be used for logging during polyline encoding operations. - /// - /// - /// The instance to use for logging. If , a will be used instead. - /// - /// - /// The current instance for method chaining. - /// - public PolylineEncodingOptionsBuilder WithLoggerFactory(ILoggerFactory loggerFactory) { - _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; - - return this; - } -} \ No newline at end of file diff --git a/src/PolylineAlgorithm/PolylineFormatter.cs b/src/PolylineAlgorithm/PolylineFormatter.cs index d2be5198..134a984d 100644 --- a/src/PolylineAlgorithm/PolylineFormatter.cs +++ b/src/PolylineAlgorithm/PolylineFormatter.cs @@ -8,62 +8,87 @@ namespace PolylineAlgorithm; using PolylineAlgorithm.Abstraction; using PolylineAlgorithm.Internal; using System; +using System.Runtime.CompilerServices; /// -/// Provides static factory methods and ready-made instances of -/// for the most common polyline surface types. +/// A sealed, immutable formatter that implements . /// +/// The coordinate or item type. +/// The polyline surface type. /// -/// -/// Use or for the two most common cases. -/// Call to build a custom formatter from a pair of delegates. -/// +/// Instances are constructed exclusively through . +/// The modifier allows the JIT to devirtualise and inline calls to the +/// interface methods in the encoding/decoding hot loop. /// -public static class PolylineFormatter { - /// - /// Gets a formatter that produces a from the encoded char buffer and reads - /// the buffer back via . - /// - public static IPolylineFormatter ForString { get; } = - new DelegatePolylineFormatter( - static mem => new string(mem.Span), - static s => s.AsMemory()); +public sealed class PolylineFormatter : IPolylineFormatter { + private readonly FormatterRule[] _rules; + private readonly PolylineItemFactory? _create; + private readonly Func, TPolyline> _write; + private readonly Func> _read; /// - /// Gets a pass-through formatter for of . - /// Both Write and Read are identity operations. + /// Initializes a new instance. Intentionally internal — use + /// to create instances. /// - public static IPolylineFormatter> ForMemory { get; } = - new DelegatePolylineFormatter>( - static mem => mem, - static mem => mem); + internal PolylineFormatter( + FormatterRule[] rules, + PolylineItemFactory? create, + Func, TPolyline> write, + Func> read) { + _rules = rules; + _create = create; + _write = write; + _read = read; + Width = rules.Length; + } - /// - /// Creates a custom from a pair of delegates. - /// - /// The polyline surface type. - /// - /// Converts the encoded of produced by the encoder - /// into a . - /// - /// - /// Extracts the encoded character buffer from a for the decoder to consume. - /// - /// A sealed backed by the supplied delegates. - /// - /// Thrown when or is . + /// + public int Width { get; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long GetBaseline(int index) => _rules[index].Baseline ?? 0L; + + /// + /// + /// Thrown when .Length does not equal . /// - public static IPolylineFormatter Create( - Func, T> write, - Func> read) { - if (write is null) { - throw new ArgumentNullException(nameof(write)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GetValues(TCoordinate item, Span values) { + if (values.Length != Width) { + throw new ArgumentException( + $"Buffer length {values.Length} does not match the formatter width {Width}.", + nameof(values)); } - if (read is null) { - throw new ArgumentNullException(nameof(read)); + var rules = _rules; + for (var i = 0; i < rules.Length; i++) { + ref var rule = ref rules[i]; + values[i] = (long)(rule.Select(item) * rule.Factor); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TPolyline Write(ReadOnlyMemory encoded) => _write(encoded); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory Read(TPolyline polyline) => _read(polyline); + + /// + /// + /// Thrown when no factory delegate was supplied via + /// . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TCoordinate CreateItem(ReadOnlySpan values) { + if (_create is null) { + throw new InvalidOperationException( + $"Cannot reconstruct an item because no factory was registered. " + + $"Call {nameof(FormatterBuilder)}.{nameof(FormatterBuilder.WithCreate)} before building."); } - return new DelegatePolylineFormatter(write, read); + return _create(values); } } diff --git a/src/PolylineAlgorithm/PolylineOptions.cs b/src/PolylineAlgorithm/PolylineOptions.cs index d0189797..922f5d19 100644 --- a/src/PolylineAlgorithm/PolylineOptions.cs +++ b/src/PolylineAlgorithm/PolylineOptions.cs @@ -5,70 +5,68 @@ namespace PolylineAlgorithm; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using PolylineAlgorithm.Abstraction; using System; /// /// Provides unified configuration for a formatter-driven encoding or decoding operation. /// -/// The coordinate or item type understood by the value formatter. -/// The polyline surface type understood by the polyline formatter. +/// The coordinate or item type understood by the formatter. +/// The polyline surface type understood by the formatter. /// -/// Combines an (which defines the column schema, scaling -/// rules, and item factory) with an (which converts between -/// the raw character buffer and the surface type) and a (which -/// controls buffer sizes, precision for legacy paths, and logging). +/// Supply an and optional settings, +/// then pass this instance to and/or +/// . /// -public sealed class PolylineOptions { +public sealed class PolylineOptions { /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// - /// - /// The formatter that defines the column schema, scaling rules, and item factory. Must not be - /// . + /// + /// The unified formatter that handles all type-specific concerns: value extraction, item + /// reconstruction, and polyline surface conversion. Must not be . /// - /// - /// The formatter that converts between the raw character buffer and - /// . Must not be . + /// + /// The maximum buffer size (in characters) for stack allocation. Defaults to 512. /// - /// - /// The encoding options that control buffer sizes, precision, and logging. - /// Pass to use default options. + /// + /// The logger factory for diagnostic logging. Pass to use + /// . /// /// - /// Thrown when or is - /// . + /// Thrown when is . /// public PolylineOptions( - IPolylineValueFormatter valueFormatter, - IPolylineFormatter polylineFormatter, - PolylineEncodingOptions? encoding = null) { - if (valueFormatter is null) { - throw new ArgumentNullException(nameof(valueFormatter)); + IPolylineFormatter formatter, + int stackAllocLimit = 512, + ILoggerFactory? loggerFactory = null) { + if (formatter is null) { + throw new ArgumentNullException(nameof(formatter)); } - if (polylineFormatter is null) { - throw new ArgumentNullException(nameof(polylineFormatter)); - } - - ValueFormatter = valueFormatter; - PolylineFormatter = polylineFormatter; - Encoding = encoding ?? new PolylineEncodingOptions(); + Formatter = formatter; + StackAllocLimit = stackAllocLimit; + LoggerFactory = loggerFactory ?? NullLoggerFactory.Instance; } /// - /// Gets the formatter that defines the column schema, scaling rules, and item factory. + /// Gets the unified formatter that handles value extraction, item reconstruction, and polyline + /// surface conversion. /// - public IPolylineValueFormatter ValueFormatter { get; } + public IPolylineFormatter Formatter { get; } /// - /// Gets the formatter that converts between the raw character buffer and - /// . + /// Gets the maximum buffer size (in characters) that may be allocated on the stack for encoding. + /// When the required buffer size exceeds this limit, memory is rented from + /// instead. Defaults to 512. /// - public IPolylineFormatter PolylineFormatter { get; } + public int StackAllocLimit { get; } /// - /// Gets the encoding options that control buffer sizes, precision, and logging. + /// Gets the logger factory used for diagnostic logging during encoding and decoding operations. + /// Defaults to . /// - public PolylineEncodingOptions Encoding { get; } + public ILoggerFactory LoggerFactory { get; } } diff --git a/src/PolylineAlgorithm/PolylineValueFormatter.cs b/src/PolylineAlgorithm/PolylineValueFormatter.cs deleted file mode 100644 index 3c24701c..00000000 --- a/src/PolylineAlgorithm/PolylineValueFormatter.cs +++ /dev/null @@ -1,130 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm; - -using PolylineAlgorithm.Abstraction; -using PolylineAlgorithm.Internal; -using System; -using System.Runtime.CompilerServices; - -/// -/// Provides an immutable, sealed rule engine that describes how to extract and scale values from -/// an object of type for polyline encoding, and how to reconstruct an -/// object of type from scaled values during decoding. -/// -/// The source object type from which column values are extracted or to which they are reconstructed. -/// -/// -/// Instances of this class are constructed exclusively through . -/// -/// -/// The modifier allows the JIT to devirtualise and inline calls to -/// and , eliminating vtable dispatch in the -/// encoding/decoding hot loop. -/// -/// -public sealed class PolylineValueFormatter : IPolylineValueFormatter { - private readonly FormatterRule[] _rules; - private readonly PolylineItemFactory? _create; - - /// - /// Initializes a new instance of with the baked rules. - /// This constructor is intentionally internal; use to create instances. - /// - /// The pre-calculated rules array produced by the builder. - /// - /// An optional factory delegate that reconstructs a from scaled values. - /// Required for the decoding direction; if , throws. - /// - internal PolylineValueFormatter(FormatterRule[] rules, PolylineItemFactory? create = null) { - _rules = rules; - _create = create; - Width = rules.Length; - HasBaselines = Array.Exists(rules, static r => r.Baseline.HasValue); - } - - /// - /// Gets the number of columns (values per item). - /// This is the required length of the passed to - /// and the length of the span received by . - /// - public int Width { get; } - - /// - /// Gets a value indicating whether any column has a baseline defined. - /// When the encoder can skip the baseline-subtraction branch entirely, - /// keeping the common-case encoding path branch-free. - /// - public bool HasBaselines { get; } - - /// - /// Gets a value indicating whether a factory delegate was supplied at build time. - /// When , calling throws an - /// . - /// - public bool CanCreateItem => _create is not null; - - /// - /// Extracts and scales all column values from into the span. - /// Called once per item in the encoding hot loop. This method performs no heap allocation; - /// the caller is responsible for providing and owning the output buffer. - /// - /// The source item from which column values are extracted. - /// - /// Output buffer that receives the scaled values. - /// Its length must equal . - /// - /// - /// Thrown when .Length does not equal . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void GetValues(T item, Span values) { - if (values.Length != Width) { - throw new ArgumentException( - $"Buffer length {values.Length} does not match the formatter width {Width}.", - nameof(values)); - } - - var rules = _rules; // local copy avoids repeated bounds check on the field - for (var i = 0; i < rules.Length; i++) { - ref var rule = ref rules[i]; - values[i] = (long)(rule.Select(item) * rule.Factor); - } - } - - /// - /// Reconstructs a from the given scaled integer values. - /// - /// - /// The accumulated scaled integer values decoded from the polyline. - /// - /// A reconstructed from . - /// - /// Thrown when no factory delegate was supplied via . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T CreateItem(ReadOnlySpan values) { - if (_create is null) { - throw new InvalidOperationException( - $"Cannot reconstruct an item because no factory was registered. " + - $"Call {nameof(FormatterBuilder)}.{nameof(FormatterBuilder.WithCreate)} before building."); - } - - return _create(values); - } - - /// - /// Returns the baseline for the column at , or 0 if none is configured. - /// The encoder subtracts this value from the first item's scaled column value during encoding. - /// - /// - /// The zero-based column index. Must be in the range [0, ). - /// An is thrown if the index is out of range. - /// - /// The baseline value, or 0 when no baseline has been defined for the column. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long GetBaseline(int index) => _rules[index].Baseline ?? 0L; -} diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index c62b223c..a9f0eaca 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -1,34 +1,46 @@ #nullable enable -PolylineAlgorithm.FormatterBuilder -PolylineAlgorithm.FormatterBuilder.AddValue(string! name, System.Func! selector, uint precision = 5) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.Build() -> PolylineAlgorithm.PolylineValueFormatter! -PolylineAlgorithm.FormatterBuilder.SetBaseline(long baseline) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.WithCreate(PolylineAlgorithm.PolylineItemFactory! create) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.Abstraction.IPolylineFormatter -PolylineAlgorithm.Abstraction.IPolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory -PolylineAlgorithm.Abstraction.IPolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline -PolylineAlgorithm.Abstraction.IPolylineValueFormatter -PolylineAlgorithm.Abstraction.IPolylineValueFormatter.CreateItem(System.ReadOnlySpan values) -> TValue -PolylineAlgorithm.Abstraction.IPolylineValueFormatter.GetBaseline(int index) -> long -PolylineAlgorithm.Abstraction.IPolylineValueFormatter.GetValues(TValue item, System.Span values) -> void -PolylineAlgorithm.Abstraction.IPolylineValueFormatter.Width.get -> int -PolylineAlgorithm.PolylineFormatter -static PolylineAlgorithm.PolylineFormatter.ForMemory.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter>! -static PolylineAlgorithm.PolylineFormatter.ForString.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! -static PolylineAlgorithm.PolylineFormatter.Create(System.Func, T>! write, System.Func>! read) -> PolylineAlgorithm.Abstraction.IPolylineFormatter! +PolylineAlgorithm.Abstraction.IPolylineFormatter +PolylineAlgorithm.Abstraction.IPolylineFormatter.CreateItem(System.ReadOnlySpan values) -> TCoordinate +PolylineAlgorithm.Abstraction.IPolylineFormatter.GetBaseline(int index) -> long +PolylineAlgorithm.Abstraction.IPolylineFormatter.GetValues(TCoordinate item, System.Span values) -> void +PolylineAlgorithm.Abstraction.IPolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory +PolylineAlgorithm.Abstraction.IPolylineFormatter.Width.get -> int +PolylineAlgorithm.Abstraction.IPolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline +PolylineAlgorithm.Abstraction.IPolylineDecoder +PolylineAlgorithm.Abstraction.IPolylineDecoder.Decode(TPolyline polyline, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable! +PolylineAlgorithm.Abstraction.IPolylineEncoder +PolylineAlgorithm.Abstraction.IPolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline +PolylineAlgorithm.FormatterBuilder +PolylineAlgorithm.FormatterBuilder.AddValue(string! name, System.Func! selector, uint precision = 5) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.FormatterBuilder.Build() -> PolylineAlgorithm.PolylineFormatter! +PolylineAlgorithm.FormatterBuilder.ForPolyline(System.Func, TPolyline>! write, System.Func>! read) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.FormatterBuilder.SetBaseline(long baseline) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.FormatterBuilder.WithCreate(PolylineAlgorithm.PolylineItemFactory! create) -> PolylineAlgorithm.FormatterBuilder! +static PolylineAlgorithm.FormatterBuilder.Create() -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.PolylineFormatter +PolylineAlgorithm.PolylineFormatter.CreateItem(System.ReadOnlySpan values) -> TCoordinate +PolylineAlgorithm.PolylineFormatter.GetBaseline(int index) -> long +PolylineAlgorithm.PolylineFormatter.GetValues(TCoordinate item, System.Span values) -> void +PolylineAlgorithm.PolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory +PolylineAlgorithm.PolylineFormatter.Width.get -> int +PolylineAlgorithm.PolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline PolylineAlgorithm.PolylineItemFactory -PolylineAlgorithm.PolylineValueFormatter -PolylineAlgorithm.PolylineValueFormatter.CanCreateItem.get -> bool -PolylineAlgorithm.PolylineValueFormatter.CreateItem(System.ReadOnlySpan values) -> T -PolylineAlgorithm.PolylineValueFormatter.GetBaseline(int index) -> long -PolylineAlgorithm.PolylineValueFormatter.GetValues(T item, System.Span values) -> void -PolylineAlgorithm.PolylineValueFormatter.HasBaselines.get -> bool -PolylineAlgorithm.PolylineValueFormatter.Width.get -> int -PolylineAlgorithm.PolylineOptions -PolylineAlgorithm.PolylineOptions.Encoding.get -> PolylineAlgorithm.PolylineEncodingOptions! -PolylineAlgorithm.PolylineOptions.PolylineFormatter.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! -PolylineAlgorithm.PolylineOptions.PolylineOptions(PolylineAlgorithm.Abstraction.IPolylineValueFormatter! valueFormatter, PolylineAlgorithm.Abstraction.IPolylineFormatter! polylineFormatter, PolylineAlgorithm.PolylineEncodingOptions? encoding = null) -> void -PolylineAlgorithm.PolylineOptions.ValueFormatter.get -> PolylineAlgorithm.Abstraction.IPolylineValueFormatter! -PolylineAlgorithm.Abstraction.AbstractPolylineEncoder.AbstractPolylineEncoder(PolylineAlgorithm.PolylineOptions! options) -> void -PolylineAlgorithm.Abstraction.AbstractPolylineDecoder.AbstractPolylineDecoder(PolylineAlgorithm.PolylineOptions! options) -> void -static PolylineAlgorithm.FormatterBuilder.Create() -> PolylineAlgorithm.FormatterBuilder! +virtual PolylineAlgorithm.PolylineItemFactory.Invoke(System.ReadOnlySpan values) -> T +PolylineAlgorithm.PolylineOptions +PolylineAlgorithm.PolylineOptions.Formatter.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! +PolylineAlgorithm.PolylineOptions.LoggerFactory.get -> Microsoft.Extensions.Logging.ILoggerFactory! +PolylineAlgorithm.PolylineOptions.PolylineOptions(PolylineAlgorithm.Abstraction.IPolylineFormatter! formatter, int stackAllocLimit = 512, Microsoft.Extensions.Logging.ILoggerFactory? loggerFactory = null) -> void +PolylineAlgorithm.PolylineOptions.StackAllocLimit.get -> int +PolylineAlgorithm.PolylineDecoder +PolylineAlgorithm.PolylineDecoder.Decode(TPolyline polyline, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable! +PolylineAlgorithm.PolylineDecoder.PolylineDecoder(PolylineAlgorithm.PolylineOptions! options) -> void +PolylineAlgorithm.PolylineEncoder +PolylineAlgorithm.PolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline +PolylineAlgorithm.PolylineEncoder.PolylineEncoder(PolylineAlgorithm.PolylineOptions! options) -> void +PolylineAlgorithm.Extensions.PolylineDecoderExtensions +PolylineAlgorithm.Extensions.PolylineEncoderExtensions +PolylineAlgorithm.InvalidPolylineException +PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException() -> void +PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException(string! message) -> void +PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException(string! message, System.Exception! innerException) -> void +PolylineAlgorithm.PolylineEncoding From 8f35f95b5982fb0356b9e1a5725dc23d06367096 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:54:10 +0000 Subject: [PATCH 09/22] fix: suppress CA1062/CA1000/RS0016/RS0017/CS1574 warnings; begin test rewrites Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/634a0417-96c9-496e-899a-de2bca4033a2 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../Extensions/PolylineDecoderExtensions.cs | 3 + .../Extensions/PolylineEncoderExtensions.cs | 2 + src/PolylineAlgorithm/FormatterBuilder.cs | 1 + .../Internal/FormatterRule.cs | 2 +- src/PolylineAlgorithm/PolylineDecoder.cs | 1 + src/PolylineAlgorithm/PolylineEncoder.cs | 1 + src/PolylineAlgorithm/PolylineItemFactory.cs | 2 +- src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 14 +- .../AbstractPolylineDecoderTests.cs | 250 +++++++---------- .../AbstractPolylineEncoderTests.cs | 263 +++++++----------- .../PolylineFormatterTests.cs | 7 +- 11 files changed, 227 insertions(+), 319 deletions(-) diff --git a/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs b/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs index ca48e099..9ef76a35 100644 --- a/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs +++ b/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs @@ -30,6 +30,7 @@ public static class PolylineDecoderExtensions { /// /// Thrown when or is . /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] public static IEnumerable Decode(this IPolylineDecoder decoder, char[] polyline) { if (decoder is null) { ExceptionGuard.ThrowArgumentNull(nameof(decoder)); @@ -58,6 +59,7 @@ public static IEnumerable Decode(this IPolylineDecoder /// Thrown when is . /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] public static IEnumerable Decode(this IPolylineDecoder decoder, ReadOnlyMemory polyline) { if (decoder is null) { ExceptionGuard.ThrowArgumentNull(nameof(decoder)); @@ -83,6 +85,7 @@ public static IEnumerable Decode(this IPolylineDecoder /// Thrown when or is . /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] public static IEnumerable Decode(this IPolylineDecoder, TValue> decoder, string polyline) { if (decoder is null) { ExceptionGuard.ThrowArgumentNull(nameof(decoder)); diff --git a/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs b/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs index e9b66923..2502dbf4 100644 --- a/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs +++ b/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs @@ -36,6 +36,7 @@ public static class PolylineEncoderExtensions { /// [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.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] public static TPolyline Encode(this IPolylineEncoder encoder, List coordinates) { if (encoder is null) { ExceptionGuard.ThrowArgumentNull(nameof(encoder)); @@ -70,6 +71,7 @@ public static TPolyline Encode(this IPolylineEncoder /// Thrown when or is . /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] public static TPolyline Encode(this IPolylineEncoder encoder, TCoordinate[] coordinates) { if (encoder is null) { ExceptionGuard.ThrowArgumentNull(nameof(encoder)); diff --git a/src/PolylineAlgorithm/FormatterBuilder.cs b/src/PolylineAlgorithm/FormatterBuilder.cs index 18edef89..15c39737 100644 --- a/src/PolylineAlgorithm/FormatterBuilder.cs +++ b/src/PolylineAlgorithm/FormatterBuilder.cs @@ -40,6 +40,7 @@ private FormatterBuilder() { } /// Creates a new instance. ///
/// A fresh builder with no rules and no polyline delegates. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Factory method on a generic builder intentionally lives on the type so callers write FormatterBuilder.Create() without needing a separate non-generic factory class.")] public static FormatterBuilder Create() => new(); /// diff --git a/src/PolylineAlgorithm/Internal/FormatterRule.cs b/src/PolylineAlgorithm/Internal/FormatterRule.cs index 9d5a4ab8..5914255f 100644 --- a/src/PolylineAlgorithm/Internal/FormatterRule.cs +++ b/src/PolylineAlgorithm/Internal/FormatterRule.cs @@ -8,7 +8,7 @@ namespace PolylineAlgorithm.Internal; using System; /// -/// Represents a single column rule baked into a . +/// Represents a single column rule baked into a . /// Stores the pre-calculated factor and an optional baseline alongside the user-supplied value selector. /// /// The source object type from which the column value is extracted. diff --git a/src/PolylineAlgorithm/PolylineDecoder.cs b/src/PolylineAlgorithm/PolylineDecoder.cs index df02e0e7..b242684e 100644 --- a/src/PolylineAlgorithm/PolylineDecoder.cs +++ b/src/PolylineAlgorithm/PolylineDecoder.cs @@ -38,6 +38,7 @@ public class PolylineDecoder : IPolylineDecoder /// Thrown when is . /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] public PolylineDecoder(PolylineOptions options) { if (options is null) { ExceptionGuard.ThrowArgumentNull(nameof(options)); diff --git a/src/PolylineAlgorithm/PolylineEncoder.cs b/src/PolylineAlgorithm/PolylineEncoder.cs index a42b942a..3386f153 100644 --- a/src/PolylineAlgorithm/PolylineEncoder.cs +++ b/src/PolylineAlgorithm/PolylineEncoder.cs @@ -40,6 +40,7 @@ public class PolylineEncoder : IPolylineEncoder /// Thrown when is . /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] public PolylineEncoder(PolylineOptions options) { if (options is null) { ExceptionGuard.ThrowArgumentNull(nameof(options)); diff --git a/src/PolylineAlgorithm/PolylineItemFactory.cs b/src/PolylineAlgorithm/PolylineItemFactory.cs index f977133e..56832992 100644 --- a/src/PolylineAlgorithm/PolylineItemFactory.cs +++ b/src/PolylineAlgorithm/PolylineItemFactory.cs @@ -14,7 +14,7 @@ namespace PolylineAlgorithm; /// The coordinate or item type to create. /// /// The scaled integer values accumulated from the polyline decoder. Each element corresponds to one -/// column as defined by the that built the associated formatter. +/// column as defined by the that built the associated formatter. /// /// A instance reconstructed from . public delegate T PolylineItemFactory(ReadOnlySpan values); diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index a9f0eaca..d01a6b46 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -38,9 +38,21 @@ PolylineAlgorithm.PolylineEncoder PolylineAlgorithm.PolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline PolylineAlgorithm.PolylineEncoder.PolylineEncoder(PolylineAlgorithm.PolylineOptions! options) -> void PolylineAlgorithm.Extensions.PolylineDecoderExtensions +static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder! decoder, char[]! polyline) -> System.Collections.Generic.IEnumerable! +static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder! decoder, System.ReadOnlyMemory polyline) -> System.Collections.Generic.IEnumerable! +static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder, TValue>! decoder, string! polyline) -> System.Collections.Generic.IEnumerable! PolylineAlgorithm.Extensions.PolylineEncoderExtensions +static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IPolylineEncoder! encoder, System.Collections.Generic.List! coordinates) -> TPolyline +static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IPolylineEncoder! encoder, TCoordinate[]! coordinates) -> TPolyline PolylineAlgorithm.InvalidPolylineException PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException() -> void -PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException(string! message) -> void PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException(string! message, System.Exception! innerException) -> void PolylineAlgorithm.PolylineEncoding +static PolylineAlgorithm.PolylineEncoding.Normalize(double value, uint precision = 5) -> int +static PolylineAlgorithm.PolylineEncoding.Denormalize(int value, uint precision = 5) -> double +static PolylineAlgorithm.PolylineEncoding.TryReadValue(ref int delta, System.ReadOnlyMemory buffer, ref int position) -> bool +static PolylineAlgorithm.PolylineEncoding.TryWriteValue(int delta, System.Span buffer, ref int position) -> bool +static PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(int delta) -> int +static PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan polyline) -> void +static PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan polyline) -> void +static PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan polyline) -> void diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs index 7e6a2472..e8176817 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs @@ -5,236 +5,172 @@ namespace PolylineAlgorithm.Tests.Abstraction; -using PolylineAlgorithm.Abstraction; using PolylineAlgorithm.Utility; using System; using System.Collections.Generic; -using PolylineAlgorithm; +using System.Threading; /// -/// Tests for . +/// Tests for . /// [TestClass] public sealed class AbstractPolylineDecoderTests { - private sealed class TestStringDecoder : AbstractPolylineDecoder { - protected override ReadOnlyMemory GetReadOnlyMemory(in string polyline) => polyline.AsMemory(); - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) => (latitude, longitude); + // ------------------------------------------------------------------ + // Helpers + // ------------------------------------------------------------------ + + private static readonly Func, string> _write = m => new string(m.Span); + private static readonly Func> _read = s => s.AsMemory(); + + private static PolylineDecoder CreateDecoder() { + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", c => c.Lat) + .AddValue("lon", c => c.Lon) + .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .ForPolyline(_write, _read) + .Build(); + + return new PolylineDecoder( + new PolylineOptions<(double Lat, double Lon), string>(formatter)); } - private sealed class TestStringDecoderWithOptions : AbstractPolylineDecoder { - public TestStringDecoderWithOptions(PolylineEncodingOptions options) - : base(options) { } + // ------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------ - protected override ReadOnlyMemory GetReadOnlyMemory(in string polyline) => polyline.AsMemory(); - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) => (latitude, longitude); + /// Tests that a null options argument throws . + [TestMethod] + public void Constructor_With_Null_Options_Throws_ArgumentNullException() { + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => new PolylineDecoder(null!)); + Assert.AreEqual("options", ex.ParamName); } - /// - /// Tests that Decode with a null polyline throws . - /// + // ------------------------------------------------------------------ + // Decode — argument validation + // ------------------------------------------------------------------ + + /// Tests that a null polyline throws . [TestMethod] public void Decode_With_Null_Polyline_Throws_ArgumentNullException() { // Arrange - TestStringDecoder decoder = new(); + PolylineDecoder decoder = CreateDecoder(); // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly(() => decoder.Decode((string?)null!).ToList()); + ArgumentNullException ex = Assert.ThrowsExactly( + () => decoder.Decode(null!).ToList()); Assert.AreEqual("polyline", ex.ParamName); } - /// - /// Tests that Decode with an empty polyline throws . - /// + /// Tests that an empty polyline throws . [TestMethod] public void Decode_With_Empty_Polyline_Throws_InvalidPolylineException() { // Arrange - TestStringDecoder decoder = new(); + PolylineDecoder decoder = CreateDecoder(); // Act & Assert - Assert.ThrowsExactly(() => decoder.Decode(string.Empty).ToList()); + Assert.ThrowsExactly( + () => decoder.Decode(string.Empty).ToList()); } /// - /// Tests that Decode with a polyline containing an invalid character throws . + /// Tests that a polyline containing a character outside the valid range throws + /// . /// [TestMethod] public void Decode_With_Invalid_Character_Polyline_Throws_InvalidPolylineException() { // Arrange - TestStringDecoder decoder = new(); + PolylineDecoder decoder = CreateDecoder(); - // '!' (33) is below allowed range ('?' == 63) + // '!' (33) is below the allowed minimum '?' (63) // Act & Assert - Assert.ThrowsExactly(() => decoder.Decode("!").ToList()); + Assert.ThrowsExactly( + () => decoder.Decode("!").ToList()); } /// - /// Tests that Decode with a valid polyline returns the expected coordinates. + /// Tests that a polyline that contains only enough data for one of the required columns + /// (i.e., a partial item) throws . /// [TestMethod] + public void Decode_With_Truncated_Polyline_Throws_InvalidPolylineException() { + // Arrange — "?" encodes a single delta-0 value. A 2-column formatter reads lat + // successfully from "?" but then finds no bytes left for lon → invalid. + PolylineDecoder decoder = CreateDecoder(); + + // Act & Assert + Assert.ThrowsExactly( + () => decoder.Decode("?").ToList()); + } + + // ------------------------------------------------------------------ + // Decode — happy path + // ------------------------------------------------------------------ + + /// Tests that a valid polyline produces the expected coordinates. + [TestMethod] public void Decode_With_Valid_Polyline_Returns_Expected_Coordinates() { // Arrange - TestStringDecoder decoder = new(); + PolylineDecoder decoder = CreateDecoder(); string polyline = StaticValueProvider.Valid.GetPolyline(); (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; // Act - (double Latitude, double Longitude)[] result = [.. decoder.Decode(polyline)]; + (double Lat, double Lon)[] result = [.. decoder.Decode(polyline)]; // Assert Assert.AreEqual(expected.Length, result.Length); for (int i = 0; i < expected.Length; i++) { - Assert.AreEqual(expected[i].Latitude, result[i].Latitude, 1e-5); - Assert.AreEqual(expected[i].Longitude, result[i].Longitude, 1e-5); + Assert.AreEqual(expected[i].Latitude, result[i].Lat, 1e-5); + Assert.AreEqual(expected[i].Longitude, result[i].Lon, 1e-5); } } - /// - /// Tests that the options constructor with null throws . - /// - [TestMethod] - public void Constructor_With_Null_Options_Throws_ArgumentNullException() { - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly(() => new TestStringDecoderWithOptions(null!)); - Assert.AreEqual("options", ex.ParamName); - } - - /// - /// Tests that the Options property returns the configured options. - /// - [TestMethod] - public void Options_With_Default_Returns_Default_Options() { - // Arrange - TestStringDecoder decoder = new(); - - // Assert - Assert.IsNotNull(decoder.Options); - Assert.AreEqual(5u, decoder.Options.Precision); - } + // ------------------------------------------------------------------ + // Decode — cancellation + // ------------------------------------------------------------------ - /// - /// Tests that the options constructor stores the provided options. - /// - [TestMethod] - public void Constructor_With_Options_Stores_Options() { - // Arrange - PolylineEncodingOptions options = PolylineEncodingOptionsBuilder.Create() - .WithPrecision(7) - .Build(); - - // Act - TestStringDecoderWithOptions decoder = new(options); - - // Assert - Assert.AreSame(options, decoder.Options); - } - - /// - /// Tests that Decode with a pre-cancelled token throws . - /// + /// Tests that a pre-cancelled token throws . [TestMethod] public void Decode_With_Pre_Cancelled_Token_Throws_OperationCanceledException() { // Arrange - TestStringDecoder decoder = new(); + PolylineDecoder decoder = CreateDecoder(); string polyline = StaticValueProvider.Valid.GetPolyline(); using CancellationTokenSource cts = new(); cts.Cancel(); // Act & Assert - Assert.ThrowsExactly(() => decoder.Decode(polyline, cts.Token).ToList()); - } - - // ------------------------------------------------------------------------- - // Formatter-based path (PolylineOptions constructor) - // ------------------------------------------------------------------------- - - /// - /// Tests that the PolylineOptions constructor with null throws . - /// - [TestMethod] - public void Constructor_With_Null_PolylineOptions_Throws_ArgumentNullException() { - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => new AbstractPolylineDecoder((PolylineOptions<(double, double), string>)null!)); - Assert.AreEqual("options", ex.ParamName); - } - - /// - /// Tests that the formatter-based decoder decodes the known polyline to the expected coordinates. - /// - [TestMethod] - public void Decode_FormatterPath_With_Known_Polyline_Returns_Expected_Coordinates() { - // Arrange - PolylineValueFormatter<(double Latitude, double Longitude)> valueFormatter = - FormatterBuilder<(double Latitude, double Longitude)>.Create() - .AddValue("lat", c => c.Latitude) - .AddValue("lon", c => c.Longitude) - .WithCreate(static values => (values[0] / 1e5, values[1] / 1e5)) - .Build(); - - PolylineOptions<(double Latitude, double Longitude), string> options = new( - valueFormatter, - PolylineFormatter.ForString); - - AbstractPolylineDecoder decoder = new(options); - - string polyline = StaticValueProvider.Valid.GetPolyline(); - (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; - - // Act - (double Latitude, double Longitude)[] result = [.. decoder.Decode(polyline)]; - - // Assert - Assert.AreEqual(expected.Length, result.Length); - for (int i = 0; i < expected.Length; i++) { - Assert.AreEqual(expected[i].Latitude, result[i].Latitude, 1e-5); - Assert.AreEqual(expected[i].Longitude, result[i].Longitude, 1e-5); - } + Assert.ThrowsExactly( + () => decoder.Decode(polyline, cts.Token).ToList()); } - /// - /// Tests that the formatter-based decoder throws for a null polyline. - /// - [TestMethod] - public void Decode_FormatterPath_With_Null_Polyline_Throws_ArgumentNullException() { - // Arrange - PolylineValueFormatter<(double, double)> valueFormatter = - FormatterBuilder<(double, double)>.Create() - .AddValue("lat", c => c.Item1) - .AddValue("lon", c => c.Item2) - .WithCreate(static values => (values[0] / 1e5, values[1] / 1e5)) - .Build(); - - AbstractPolylineDecoder decoder = new( - new PolylineOptions<(double, double), string>(valueFormatter, PolylineFormatter.ForString)); - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => decoder.Decode(null!).ToList()); - Assert.AreEqual("polyline", ex.ParamName); - } + // ------------------------------------------------------------------ + // Decode — missing WithCreate factory + // ------------------------------------------------------------------ /// - /// Tests that the formatter-based decoder with a pre-cancelled token throws . + /// Tests that decoding with a formatter that has no WithCreate factory throws + /// . /// [TestMethod] - public void Decode_FormatterPath_With_Pre_Cancelled_Token_Throws_OperationCanceledException() { - // Arrange - PolylineValueFormatter<(double, double)> valueFormatter = - FormatterBuilder<(double, double)>.Create() - .AddValue("lat", c => c.Item1) - .AddValue("lon", c => c.Item2) - .WithCreate(static values => (values[0] / 1e5, values[1] / 1e5)) + public void Decode_Without_WithCreate_Throws_InvalidOperationException() { + // Arrange — formatter built without WithCreate + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", c => c.Lat) + .AddValue("lon", c => c.Lon) + .ForPolyline(_write, _read) .Build(); - AbstractPolylineDecoder decoder = new( - new PolylineOptions<(double, double), string>(valueFormatter, PolylineFormatter.ForString)); + PolylineDecoder decoder = + new(new PolylineOptions<(double Lat, double Lon), string>(formatter)); string polyline = StaticValueProvider.Valid.GetPolyline(); - using CancellationTokenSource cts = new(); - cts.Cancel(); // Act & Assert - Assert.ThrowsExactly( - () => decoder.Decode(polyline, cts.Token).ToList()); + Assert.ThrowsExactly( + () => decoder.Decode(polyline).ToList()); } } diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs index 0403fa1f..7f8d270a 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs @@ -5,89 +5,81 @@ namespace PolylineAlgorithm.Tests.Abstraction; -using PolylineAlgorithm.Abstraction; using PolylineAlgorithm.Utility; using System; -using PolylineAlgorithm; +using System.Threading; /// -/// Tests for . +/// Tests for . /// [TestClass] public sealed class AbstractPolylineEncoderTests { - private sealed class TestStringEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string> { - public TestStringEncoder() - : base() { } + // ------------------------------------------------------------------ + // Helpers + // ------------------------------------------------------------------ - public TestStringEncoder(PolylineEncodingOptions options) - : base(options) { } + private static readonly Func, string> _write = m => new string(m.Span); + private static readonly Func> _read = s => s.AsMemory(); - protected override string CreatePolyline(ReadOnlyMemory polyline) => polyline.ToString(); - protected override double GetLatitude((double Latitude, double Longitude) current) => current.Latitude; - protected override double GetLongitude((double Latitude, double Longitude) current) => current.Longitude; - } - - /// - /// Tests that the default constructor creates an instance with default options. - /// - [TestMethod] - public void Constructor_With_Default_Options_Creates_Instance() { - // Act - TestStringEncoder encoder = new(); + private static PolylineEncoder<(double Lat, double Lon), string> CreateEncoder(int stackAllocLimit = 512) { + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", c => c.Lat) + .AddValue("lon", c => c.Lon) + .ForPolyline(_write, _read) + .Build(); - // Assert - Assert.IsNotNull(encoder); - Assert.IsNotNull(encoder.Options); - Assert.AreEqual(5u, encoder.Options.Precision); - Assert.AreEqual(512, encoder.Options.StackAllocLimit); + return new PolylineEncoder<(double Lat, double Lon), string>( + new PolylineOptions<(double Lat, double Lon), string>(formatter, stackAllocLimit)); } - /// - /// Tests that the options constructor with null throws . - /// + // ------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------ + + /// Tests that a null options argument throws . [TestMethod] public void Constructor_With_Null_Options_Throws_ArgumentNullException() { // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly(() => new TestStringEncoder(null!)); + ArgumentNullException ex = Assert.ThrowsExactly( + () => new PolylineEncoder<(double, double), string>(null!)); Assert.AreEqual("options", ex.ParamName); } - /// - /// Tests that the options constructor stores the provided options. - /// + /// Tests that a valid options argument creates the instance without throwing. [TestMethod] - public void Constructor_With_Options_Stores_Options() { - // Arrange - PolylineEncodingOptions options = PolylineEncodingOptionsBuilder.Create() - .WithPrecision(7) - .Build(); - + public void Constructor_With_Valid_Options_Creates_Instance() { // Act - TestStringEncoder encoder = new(options); + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); // Assert - Assert.AreSame(options, encoder.Options); + Assert.IsNotNull(encoder); } - /// - /// Tests that Encode with an empty span throws . - /// + // ------------------------------------------------------------------ + // Encode — argument validation + // ------------------------------------------------------------------ + + /// Tests that encoding an empty span throws . [TestMethod] public void Encode_With_Empty_Span_Throws_ArgumentException() { // Arrange - TestStringEncoder encoder = new(); + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); // Act & Assert - Assert.ThrowsExactly(() => encoder.Encode(ReadOnlySpan<(double, double)>.Empty)); + Assert.ThrowsExactly( + () => encoder.Encode(ReadOnlySpan<(double, double)>.Empty)); } - /// - /// Tests that Encode with a single valid coordinate returns a non-empty string. - /// + // ------------------------------------------------------------------ + // Encode — happy path + // ------------------------------------------------------------------ + + /// Tests that a single coordinate encodes to a non-empty string. [TestMethod] public void Encode_With_Single_Coordinate_Returns_Non_Empty_String() { // Arrange - TestStringEncoder encoder = new(); + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); (double, double)[] coordinates = [(0.0, 0.0)]; // Act @@ -98,14 +90,12 @@ public void Encode_With_Single_Coordinate_Returns_Non_Empty_String() { Assert.IsTrue(result.Length > 0); } - /// - /// Tests that Encode with known coordinates returns the expected polyline string. - /// + /// Tests that known coordinates produce the expected polyline string. [TestMethod] public void Encode_With_Known_Coordinates_Returns_Expected_Polyline() { // Arrange - TestStringEncoder encoder = new(); - (double Latitude, double Longitude)[] coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); + (double Lat, double Lon)[] coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; string expected = StaticValueProvider.Valid.GetPolyline(); // Act @@ -115,33 +105,37 @@ public void Encode_With_Known_Coordinates_Returns_Expected_Polyline() { Assert.AreEqual(expected, result); } - /// - /// Tests that Encode with a pre-cancelled token throws . - /// + // ------------------------------------------------------------------ + // Encode — cancellation + // ------------------------------------------------------------------ + + /// Tests that a pre-cancelled token throws . [TestMethod] public void Encode_With_Pre_Cancelled_Token_Throws_OperationCanceledException() { // Arrange - TestStringEncoder encoder = new(); + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); using CancellationTokenSource cts = new(); cts.Cancel(); (double, double)[] coordinates = [(0.0, 0.0), (1.0, 1.0)]; // Act & Assert - Assert.ThrowsExactly(() => encoder.Encode(coordinates.AsSpan(), cts.Token)); + Assert.ThrowsExactly( + () => encoder.Encode(coordinates.AsSpan(), cts.Token)); } + // ------------------------------------------------------------------ + // Encode — heap allocation path + // ------------------------------------------------------------------ + /// - /// Tests that Encode still produces the correct result when the buffer exceeds the stack allocation - /// limit, forcing heap allocation via . + /// Tests that a stack-alloc limit of 1 forces heap allocation via ArrayPool + /// but still produces the correct result. /// [TestMethod] - public void Encode_With_Small_Stack_Alloc_Limit_Uses_Heap_Allocation_And_Produces_Correct_Result() { - // Arrange — force heap path by making stackAllocLimit smaller than any real encoding needs - PolylineEncodingOptions options = PolylineEncodingOptionsBuilder.Create() - .WithStackAllocLimit(1) - .Build(); - TestStringEncoder encoder = new(options); - (double Latitude, double Longitude)[] coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; + public void Encode_With_Small_Stack_Alloc_Limit_Uses_Heap_And_Produces_Correct_Result() { + // Arrange — limit of 1 forces every encode to go through ArrayPool + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(stackAllocLimit: 1); + (double Lat, double Lon)[] coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; string expected = StaticValueProvider.Valid.GetPolyline(); // Act @@ -151,125 +145,80 @@ public void Encode_With_Small_Stack_Alloc_Limit_Uses_Heap_Allocation_And_Produce Assert.AreEqual(expected, result); } - // ------------------------------------------------------------------------- - // Formatter-based path (PolylineOptions constructor) - // ------------------------------------------------------------------------- - - /// - /// Tests that the PolylineOptions constructor with null throws . - /// - [TestMethod] - public void Constructor_With_Null_PolylineOptions_Throws_ArgumentNullException() { - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => new AbstractPolylineEncoder<(double, double), string>((PolylineOptions<(double, double), string>)null!)); - Assert.AreEqual("options", ex.ParamName); - } + // ------------------------------------------------------------------ + // Encode — baseline + // ------------------------------------------------------------------ /// - /// Tests that the formatter-based encoder round-trips known coordinates through the polyline wire format. + /// Tests that a non-zero baseline shifts the first delta so the result differs + /// from the same encode without a baseline. /// [TestMethod] - public void Encode_FormatterPath_With_Known_Coordinates_Returns_Expected_Polyline() { + public void Encode_With_Baseline_Produces_Different_First_Delta() { // Arrange - PolylineValueFormatter<(double Latitude, double Longitude)> valueFormatter = - FormatterBuilder<(double Latitude, double Longitude)>.Create() - .AddValue("lat", c => c.Latitude) - .AddValue("lon", c => c.Longitude) + PolylineFormatter<(double, double), string> formatterWithBaseline = + FormatterBuilder<(double, double), string>.Create() + .AddValue("lat", c => c.Item1).SetBaseline(100000L) // scaled 1.0 at precision 5 + .AddValue("lon", c => c.Item2) + .ForPolyline(_write, _read) .Build(); - PolylineOptions<(double Latitude, double Longitude), string> options = new( - valueFormatter, - PolylineFormatter.ForString); - - AbstractPolylineEncoder<(double Latitude, double Longitude), string> encoder = new(options); - - (double Latitude, double Longitude)[] coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; - string expected = StaticValueProvider.Valid.GetPolyline(); - - // Act - string result = encoder.Encode(coordinates.AsSpan()); - - // Assert - Assert.AreEqual(expected, result); - } - - /// - /// Tests that the formatter-based encoder respects a non-zero baseline by subtracting it - /// from the first item's scaled value, producing a smaller first delta. - /// - [TestMethod] - public void Encode_FormatterPath_With_Baseline_Produces_Correct_First_Delta() { - // Arrange: a single coordinate at (1.0, 2.0) with precision 5. - // Without baseline, scaled values are (100000, 200000). - // With baseline lat=100000, the first lat delta must be 0 (100000 - 100000). - PolylineValueFormatter<(double Lat, double Lon)> valueFormatter = - FormatterBuilder<(double Lat, double Lon)>.Create() - .AddValue("lat", c => c.Lat) - .SetBaseline(100000L) // scaled value of 1.0 at precision 5 - .AddValue("lon", c => c.Lon) - .Build(); - - PolylineOptions<(double Lat, double Lon), string> optionsWithBaseline = new( - valueFormatter, - PolylineFormatter.ForString); - - PolylineValueFormatter<(double Lat, double Lon)> valueFormatterNoBaseline = - FormatterBuilder<(double Lat, double Lon)>.Create() - .AddValue("lat", c => c.Lat) - .AddValue("lon", c => c.Lon) + PolylineFormatter<(double, double), string> formatterNoBaseline = + FormatterBuilder<(double, double), string>.Create() + .AddValue("lat", c => c.Item1) + .AddValue("lon", c => c.Item2) + .ForPolyline(_write, _read) .Build(); - PolylineOptions<(double Lat, double Lon), string> optionsNoBaseline = new( - valueFormatterNoBaseline, - PolylineFormatter.ForString); - - AbstractPolylineEncoder<(double Lat, double Lon), string> encoderWithBaseline = new(optionsWithBaseline); - AbstractPolylineEncoder<(double Lat, double Lon), string> encoderNoBaseline = new(optionsNoBaseline); + PolylineEncoder<(double, double), string> encoderWithBaseline = + new(new PolylineOptions<(double, double), string>(formatterWithBaseline)); + PolylineEncoder<(double, double), string> encoderNoBaseline = + new(new PolylineOptions<(double, double), string>(formatterNoBaseline)); (double, double)[] coordinates = [(1.0, 2.0)]; // Act - string resultWithBaseline = encoderWithBaseline.Encode(coordinates.AsSpan()); - string resultNoBaseline = encoderNoBaseline.Encode(coordinates.AsSpan()); + string resultWith = encoderWithBaseline.Encode(coordinates.AsSpan()); + string resultWithout = encoderNoBaseline.Encode(coordinates.AsSpan()); - // Assert: baseline shifts the first delta so the results differ, and the baseline result - // encodes a smaller (zero) latitude delta. - Assert.AreNotEqual(resultNoBaseline, resultWithBaseline); + // Assert — the two encodings must differ because the first lat delta changes + Assert.AreNotEqual(resultWithout, resultWith); } + // ------------------------------------------------------------------ + // Round-trip + // ------------------------------------------------------------------ + /// - /// Tests that the formatter-based encoder produces output that the formatter-based decoder can - /// reconstruct exactly (full round-trip). + /// Tests that encoding then decoding reproduces the original coordinates within + /// the precision of the encoding. /// [TestMethod] - public void Encode_FormatterPath_RoundTrip_Produces_Original_Coordinates() { + public void Encode_RoundTrip_Produces_Original_Coordinates() { // Arrange - PolylineValueFormatter<(double Latitude, double Longitude)> valueFormatter = - FormatterBuilder<(double Latitude, double Longitude)>.Create() - .AddValue("lat", c => c.Latitude) - .AddValue("lon", c => c.Longitude) - .WithCreate(static values => (values[0] / 1e5, values[1] / 1e5)) + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", c => c.Lat) + .AddValue("lon", c => c.Lon) + .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .ForPolyline(_write, _read) .Build(); - PolylineOptions<(double Latitude, double Longitude), string> options = new( - valueFormatter, - PolylineFormatter.ForString); - - AbstractPolylineEncoder<(double Latitude, double Longitude), string> encoder = new(options); - AbstractPolylineDecoder decoder = new(options); + PolylineOptions<(double Lat, double Lon), string> options = new(formatter); + PolylineEncoder<(double Lat, double Lon), string> encoder = new(options); + PolylineDecoder decoder = new(options); - (double Latitude, double Longitude)[] original = [.. StaticValueProvider.Valid.GetCoordinates()]; + (double Lat, double Lon)[] original = [.. StaticValueProvider.Valid.GetCoordinates()]; // Act string encoded = encoder.Encode(original.AsSpan()); - (double Latitude, double Longitude)[] decoded = [.. decoder.Decode(encoded)]; + (double Lat, double Lon)[] decoded = [.. decoder.Decode(encoded)]; // Assert Assert.AreEqual(original.Length, decoded.Length); for (int i = 0; i < original.Length; i++) { - Assert.AreEqual(original[i].Latitude, decoded[i].Latitude, 1e-5); - Assert.AreEqual(original[i].Longitude, decoded[i].Longitude, 1e-5); + Assert.AreEqual(original[i].Lat, decoded[i].Lat, 1e-5); + Assert.AreEqual(original[i].Lon, decoded[i].Lon, 1e-5); } } } diff --git a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs index 7fbfd1da..537c48fc 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs @@ -9,11 +9,14 @@ namespace PolylineAlgorithm.Tests; using System; /// -/// Tests for , , -/// and . +/// Tests for and +/// . /// [TestClass] public sealed class PolylineFormatterTests { + // Shared ForPolyline delegates used to satisfy Build() throughout these tests. + private static readonly Func, string> _write = m => new string(m.Span); + private static readonly Func> _read = s => s.AsMemory(); // --------------------------------------------------------------------------- // FormatterBuilder.Create // --------------------------------------------------------------------------- From 3b971cc32009387beb01e399097515b926fd7e40 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:45:15 +0000 Subject: [PATCH 10/22] fix: update all callers of AbstractPolylineEncoder/Decoder and PolylineEncodingOptions to new API Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/01588b5e-a054-44f2-8639-946d11b55609 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../PolylineDecoderBenchmark.cs | 77 +-- .../PolylineEncoderBenchmark.cs | 29 +- .../NetTopologyPolylineDecoder.cs | 41 +- .../NetTopologyPolylineEncoder.cs | 52 +-- .../SensorDataDecoder.cs | 14 +- .../SensorDataEncoder.cs | 14 +- .../SensorEncodingOptions.cs | 39 ++ .../PolylineDecoderExtensionsTests.cs | 39 +- .../PolylineEncoderExtensionsTests.cs | 23 +- .../PolylineEncodingOptionsBuilderTests.cs | 437 +++++------------- .../PolylineFormatterTests.cs | 218 ++++----- .../RandomValueProvider.cs | 29 +- 12 files changed, 411 insertions(+), 601 deletions(-) create mode 100644 samples/PolylineAlgorithm.SensorData.Sample/SensorEncodingOptions.cs diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs index f28c8141..0195da78 100644 --- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs +++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs @@ -7,11 +7,11 @@ namespace PolylineAlgorithm.Benchmarks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Engines; -using PolylineAlgorithm.Abstraction; +using PolylineAlgorithm; using PolylineAlgorithm.Utility; /// -/// Benchmarks for . +/// Benchmarks for . /// public class PolylineDecoderBenchmark { private readonly Consumer _consumer = new(); @@ -40,17 +40,50 @@ public class PolylineDecoderBenchmark { /// /// String polyline decoder instance. /// - private readonly StringPolylineDecoder _stringDecoder = new(); + private readonly PolylineDecoder _stringDecoder = CreateStringDecoder(); /// /// Char array polyline decoder instance. /// - private readonly CharArrayPolylineDecoder _charArrayDecoder = new(); + private readonly PolylineDecoder _charArrayDecoder = CreateCharArrayDecoder(); /// - /// String polyline decoder instance. + /// Memory char polyline decoder instance. /// - private readonly MemoryCharPolylineDecoder _memoryCharDecoder = new(); + private readonly PolylineDecoder, (double Latitude, double Longitude)> _memoryCharDecoder = CreateMemoryDecoder(); + + private static PolylineFormatter<(double Latitude, double Longitude), T> BuildFormatter( + Func, T> write, Func> read) => + FormatterBuilder<(double Latitude, double Longitude), T>.Create() + .AddValue("lat", static c => c.Latitude) + .AddValue("lon", static c => c.Longitude) + .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .ForPolyline(write, read) + .Build(); + + private static PolylineDecoder CreateStringDecoder() { + var fmt = BuildFormatter( + static m => new string(m.Span), + static s => s?.AsMemory() ?? Memory.Empty); + return new PolylineDecoder( + new PolylineOptions<(double Latitude, double Longitude), string>(fmt)); + } + + private static PolylineDecoder CreateCharArrayDecoder() { + var fmt = BuildFormatter( + static m => m.ToArray(), + static a => a?.AsMemory() ?? Memory.Empty); + return new PolylineDecoder( + new PolylineOptions<(double Latitude, double Longitude), char[]>(fmt)); + } + + private static PolylineDecoder, (double Latitude, double Longitude)> CreateMemoryDecoder() { + var fmt = BuildFormatter>( + static m => m, + static m => m); + return new PolylineDecoder, (double Latitude, double Longitude)>( + new PolylineOptions<(double Latitude, double Longitude), ReadOnlyMemory>(fmt)); + } /// /// Sets up benchmark data. @@ -91,34 +124,4 @@ public void PolylineDecoder_Decode_Memory() { .Decode(Memory) .Consume(_consumer); } - - private sealed class StringPolylineDecoder : AbstractPolylineDecoder { - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) { - return (latitude, longitude); - } - - protected override ReadOnlyMemory GetReadOnlyMemory(in string polyline) { - return polyline?.AsMemory() ?? Memory.Empty; - } - } - - private sealed class CharArrayPolylineDecoder : AbstractPolylineDecoder { - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) { - return (latitude, longitude); - } - - protected override ReadOnlyMemory GetReadOnlyMemory(in char[] polyline) { - return polyline?.AsMemory() ?? Memory.Empty; - } - } - - private sealed class MemoryCharPolylineDecoder : AbstractPolylineDecoder, (double Latitude, double Longitude)> { - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) { - return (latitude, longitude); - } - - protected override ReadOnlyMemory GetReadOnlyMemory(in ReadOnlyMemory polyline) { - return polyline; - } - } -} \ No newline at end of file +} diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs index e0b97c5c..f7471463 100644 --- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs +++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs @@ -1,19 +1,14 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - namespace PolylineAlgorithm.Benchmarks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Engines; -using PolylineAlgorithm.Abstraction; +using PolylineAlgorithm; using PolylineAlgorithm.Extensions; using PolylineAlgorithm.Utility; using System.Collections.Generic; /// -/// Benchmarks for . +/// Benchmarks for . /// public class PolylineEncoderBenchmark { private readonly Consumer _consumer = new(); @@ -45,7 +40,19 @@ public class PolylineEncoderBenchmark { /// /// Polyline encoder instance. /// - private readonly StringPolylineEncoder _encoder = new(); + private readonly PolylineEncoder<(double Latitude, double Longitude), string> _encoder = CreateEncoder(); + + private static PolylineEncoder<(double Latitude, double Longitude), string> CreateEncoder() { + PolylineFormatter<(double Latitude, double Longitude), string> formatter = + FormatterBuilder<(double Latitude, double Longitude), string>.Create() + .AddValue("lat", static c => c.Latitude) + .AddValue("lon", static c => c.Longitude) + .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .Build(); + + return new PolylineEncoder<(double Latitude, double Longitude), string>( + new PolylineOptions<(double Latitude, double Longitude), string>(formatter)); + } /// /// Sets up benchmark data. @@ -83,10 +90,4 @@ public void PolylineEncoder_Encode_List() { var polyline = _encoder.Encode(List); _consumer.Consume(polyline); } - - private sealed class StringPolylineEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string> { - protected override string CreatePolyline(ReadOnlyMemory polyline) => polyline.ToString(); - protected override double GetLatitude((double Latitude, double Longitude) current) => current.Latitude; - protected override double GetLongitude((double Latitude, double Longitude) current) => current.Longitude; - } } diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs index d7dcb13e..408f44e4 100644 --- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs +++ b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs @@ -6,30 +6,37 @@ namespace PolylineAlgorithm.NetTopologySuite.Sample; using global::NetTopologySuite.Geometries; +using PolylineAlgorithm; using PolylineAlgorithm.Abstraction; -using System; +using System.Collections.Generic; +using System.Threading; /// /// Polyline decoder using NetTopologySuite. /// -internal sealed class NetTopologyPolylineDecoder : AbstractPolylineDecoder { - /// - /// Creates a NetTopologySuite point from latitude and longitude. - /// - /// Latitude value. - /// Longitude value. - /// Point instance. - protected override Point CreateCoordinate(double latitude, double longitude) { - // NetTopologySuite Point: x = longitude, y = latitude - return new Point(longitude, latitude); - } +internal sealed class NetTopologyPolylineDecoder : IPolylineDecoder { + private readonly PolylineDecoder _inner; /// - /// Converts polyline string to read-only memory. + /// Initializes a new instance of the class. /// - /// Encoded polyline string. - /// ReadOnlyMemory of characters. - protected override ReadOnlyMemory GetReadOnlyMemory(in string polyline) { - return polyline.AsMemory(); + internal NetTopologyPolylineDecoder() { + PolylineFormatter formatter = + FormatterBuilder.Create() + // NetTopologySuite Point: Y = latitude, X = longitude + .AddValue("lat", static p => p.Y) + .AddValue("lon", static p => p.X) + // v[0] = scaled latitude, v[1] = scaled longitude (factor = 1e5 for default precision 5) + .WithCreate(static v => new Point(x: v[1] / 1e5, y: v[0] / 1e5)) + .ForPolyline( + static m => m.IsEmpty ? string.Empty : new string(m.Span), + static s => s.AsMemory()) + .Build(); + + _inner = new PolylineDecoder(new PolylineOptions(formatter)); } + + /// + public IEnumerable Decode(string polyline, CancellationToken cancellationToken = default) + => _inner.Decode(polyline, cancellationToken); } \ No newline at end of file diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs index c6719000..7eb0892b 100644 --- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs +++ b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs @@ -6,46 +6,34 @@ namespace PolylineAlgorithm.NetTopologySuite.Sample; using global::NetTopologySuite.Geometries; +using PolylineAlgorithm; using PolylineAlgorithm.Abstraction; +using System.Threading; /// /// Polyline encoder using NetTopologySuite's Point type. /// -internal sealed class NetTopologyPolylineEncoder : AbstractPolylineEncoder { - /// - /// Creates encoded polyline string from memory. - /// - /// Polyline memory. - /// Encoded polyline string. - protected override string CreatePolyline(ReadOnlyMemory polyline) { - if (polyline.IsEmpty) { - return string.Empty; - } - - return polyline.ToString(); - } +internal sealed class NetTopologyPolylineEncoder : IPolylineEncoder { + private readonly PolylineEncoder _inner; /// - /// Gets latitude from point. + /// Initializes a new instance of the class. /// - /// Point instance. - /// Latitude value. - protected override double GetLatitude(Point current) { - ArgumentNullException.ThrowIfNull(current); - - // NetTopologySuite Point: Y = latitude - return current.Y; + internal NetTopologyPolylineEncoder() { + PolylineFormatter formatter = + FormatterBuilder.Create() + // NetTopologySuite Point: Y = latitude, X = longitude + .AddValue("lat", static p => { ArgumentNullException.ThrowIfNull(p); return p.Y; }) + .AddValue("lon", static p => { ArgumentNullException.ThrowIfNull(p); return p.X; }) + .ForPolyline( + static m => m.IsEmpty ? string.Empty : new string(m.Span), + static s => s.AsMemory()) + .Build(); + + _inner = new PolylineEncoder(new PolylineOptions(formatter)); } - /// - /// Gets longitude from point. - /// - /// Point instance. - /// Longitude value. - protected override double GetLongitude(Point current) { - ArgumentNullException.ThrowIfNull(current); - - // NetTopologySuite Point: X = longitude - return current.X; - } + /// + public string Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) + => _inner.Encode(coordinates, cancellationToken); } \ No newline at end of file diff --git a/samples/PolylineAlgorithm.SensorData.Sample/SensorDataDecoder.cs b/samples/PolylineAlgorithm.SensorData.Sample/SensorDataDecoder.cs index 9c63d647..d3fa0413 100644 --- a/samples/PolylineAlgorithm.SensorData.Sample/SensorDataDecoder.cs +++ b/samples/PolylineAlgorithm.SensorData.Sample/SensorDataDecoder.cs @@ -16,11 +16,11 @@ namespace PolylineAlgorithm.SensorData.Sample; /// /// /// This class demonstrates implementing for a custom -/// scalar type, following the same structural pattern as . +/// scalar type, following the same structural pattern as a custom polyline decoder. /// /// /// Each encoded pair consists of a delta-compressed Unix timestamp (seconds since Unix epoch, precision 0) -/// followed by a delta-compressed temperature value (at ). +/// followed by a delta-compressed temperature value (at ). /// Both are recovered and used to reconstruct the original . /// /// @@ -30,19 +30,19 @@ internal sealed class SensorDataDecoder : IPolylineDecoder class with default encoding options. /// public SensorDataDecoder() - : this(new PolylineEncodingOptions()) { } + : this(new SensorEncodingOptions()) { } /// /// Initializes a new instance of the class with the specified encoding options. /// /// - /// The to use for decoding operations. - /// The value must match the precision used during encoding. + /// The to use for decoding operations. + /// The value must match the precision used during encoding. /// /// /// Thrown when is . /// - public SensorDataDecoder(PolylineEncodingOptions options) { + public SensorDataDecoder(SensorEncodingOptions options) { ArgumentNullException.ThrowIfNull(options); Options = options; @@ -51,7 +51,7 @@ public SensorDataDecoder(PolylineEncodingOptions options) { /// /// Gets the encoding options used by this decoder. /// - public PolylineEncodingOptions Options { get; } + public SensorEncodingOptions Options { get; } /// /// Decodes a polyline string back into a sequence of values. diff --git a/samples/PolylineAlgorithm.SensorData.Sample/SensorDataEncoder.cs b/samples/PolylineAlgorithm.SensorData.Sample/SensorDataEncoder.cs index be199e4d..872bd318 100644 --- a/samples/PolylineAlgorithm.SensorData.Sample/SensorDataEncoder.cs +++ b/samples/PolylineAlgorithm.SensorData.Sample/SensorDataEncoder.cs @@ -17,16 +17,16 @@ namespace PolylineAlgorithm.SensorData.Sample; /// /// /// This class demonstrates implementing for a custom -/// scalar type, following the same structural pattern as . +/// scalar type, following the same structural pattern as a custom polyline encoder. /// /// -/// Because sensor readings carry two numeric dimensions (timestamp and temperature), the base class designed +/// Because sensor readings carry two numeric dimensions (timestamp and temperature), the generic encoder /// for geographic coordinate pairs is not used. Instead, static /// helpers are called directly to perform normalisation, delta computation, and character-level encoding. /// /// /// Each reading is encoded as a pair of delta-compressed values: -/// the Unix timestamp in seconds (precision 0) followed by the temperature (at ). +/// the Unix timestamp in seconds (precision 0) followed by the temperature (at ). /// /// internal sealed class SensorDataEncoder : IPolylineEncoder { @@ -38,18 +38,18 @@ internal sealed class SensorDataEncoder : IPolylineEncoder class with default encoding options. /// public SensorDataEncoder() - : this(new PolylineEncodingOptions()) { } + : this(new SensorEncodingOptions()) { } /// /// Initializes a new instance of the class with the specified encoding options. /// /// - /// The to use for encoding operations. + /// The to use for encoding operations. /// /// /// Thrown when is . /// - public SensorDataEncoder(PolylineEncodingOptions options) { + public SensorDataEncoder(SensorEncodingOptions options) { ArgumentNullException.ThrowIfNull(options); Options = options; @@ -58,7 +58,7 @@ public SensorDataEncoder(PolylineEncodingOptions options) { /// /// Gets the encoding options used by this encoder. /// - public PolylineEncodingOptions Options { get; } + public SensorEncodingOptions Options { get; } /// /// Encodes a sequence of values into a polyline string. diff --git a/samples/PolylineAlgorithm.SensorData.Sample/SensorEncodingOptions.cs b/samples/PolylineAlgorithm.SensorData.Sample/SensorEncodingOptions.cs new file mode 100644 index 00000000..5ad66387 --- /dev/null +++ b/samples/PolylineAlgorithm.SensorData.Sample/SensorEncodingOptions.cs @@ -0,0 +1,39 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm.SensorData.Sample; + +/// +/// Encoding options used by and . +/// +internal sealed class SensorEncodingOptions { + /// + /// Initializes a new instance of the class with default values. + /// + public SensorEncodingOptions() + : this(precision: 5, stackAllocLimit: 512) { } + + /// + /// Initializes a new instance of the class. + /// + /// Number of decimal places to use when encoding floating-point values. Defaults to 5. + /// + /// Maximum number of characters to allocate on the stack during encoding. Defaults to 512. + /// + public SensorEncodingOptions(uint precision, int stackAllocLimit) { + Precision = precision; + StackAllocLimit = stackAllocLimit; + } + + /// + /// Gets the number of decimal places used when encoding floating-point values. + /// + public uint Precision { get; } + + /// + /// Gets the maximum number of characters to allocate on the stack during encoding. + /// + public int StackAllocLimit { get; } +} diff --git a/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs index c44c914d..c4ec4879 100644 --- a/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs +++ b/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs @@ -5,6 +5,7 @@ namespace PolylineAlgorithm.Tests.Extensions; +using PolylineAlgorithm; using PolylineAlgorithm.Abstraction; using PolylineAlgorithm.Extensions; using PolylineAlgorithm.Utility; @@ -16,14 +17,30 @@ namespace PolylineAlgorithm.Tests.Extensions; /// [TestClass] public sealed class PolylineDecoderExtensionsTests { - private sealed class TestStringDecoder : AbstractPolylineDecoder { - protected override ReadOnlyMemory GetReadOnlyMemory(in string polyline) => polyline.AsMemory(); - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) => (latitude, longitude); + private static PolylineDecoder CreateStringDecoder() { + PolylineFormatter<(double Latitude, double Longitude), string> formatter = + FormatterBuilder<(double Latitude, double Longitude), string>.Create() + .AddValue("lat", static c => c.Latitude) + .AddValue("lon", static c => c.Longitude) + .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .Build(); + + return new PolylineDecoder( + new PolylineOptions<(double Latitude, double Longitude), string>(formatter)); } - private sealed class TestMemoryDecoder : AbstractPolylineDecoder, (double Latitude, double Longitude)> { - protected override ReadOnlyMemory GetReadOnlyMemory(in ReadOnlyMemory polyline) => polyline; - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) => (latitude, longitude); + private static PolylineDecoder, (double Latitude, double Longitude)> CreateMemoryDecoder() { + PolylineFormatter<(double Latitude, double Longitude), ReadOnlyMemory> formatter = + FormatterBuilder<(double Latitude, double Longitude), ReadOnlyMemory>.Create() + .AddValue("lat", static c => c.Latitude) + .AddValue("lon", static c => c.Longitude) + .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .ForPolyline(static m => m, static m => m) + .Build(); + + return new PolylineDecoder, (double Latitude, double Longitude)>( + new PolylineOptions<(double Latitude, double Longitude), ReadOnlyMemory>(formatter)); } // ----- Decode(char[]) for IPolylineDecoder ----- @@ -49,7 +66,7 @@ public void Decode_With_Char_Array_Null_Decoder_Throws_ArgumentNullException() { [TestMethod] public void Decode_With_Char_Array_Null_Polyline_Throws_ArgumentNullException() { // Arrange - TestStringDecoder decoder = new(); + var decoder = CreateStringDecoder(); char[]? polyline = null; // Act & Assert @@ -64,7 +81,7 @@ public void Decode_With_Char_Array_Null_Polyline_Throws_ArgumentNullException() [TestMethod] public void Decode_With_Char_Array_Valid_Polyline_Returns_Expected_Coordinates() { // Arrange - TestStringDecoder decoder = new(); + var decoder = CreateStringDecoder(); char[] polyline = StaticValueProvider.Valid.GetPolyline().ToCharArray(); (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; @@ -102,7 +119,7 @@ public void Decode_With_Memory_Null_Decoder_Throws_ArgumentNullException() { [TestMethod] public void Decode_With_Memory_Valid_Polyline_Returns_Expected_Coordinates() { // Arrange - TestStringDecoder decoder = new(); + var decoder = CreateStringDecoder(); ReadOnlyMemory polyline = StaticValueProvider.Valid.GetPolyline().AsMemory(); (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; @@ -140,7 +157,7 @@ public void Decode_With_String_Null_Decoder_Throws_ArgumentNullException() { [TestMethod] public void Decode_With_String_Null_Polyline_Throws_ArgumentNullException() { // Arrange - TestMemoryDecoder decoder = new(); + var decoder = CreateMemoryDecoder(); string? polyline = null; // Act & Assert @@ -155,7 +172,7 @@ public void Decode_With_String_Null_Polyline_Throws_ArgumentNullException() { [TestMethod] public void Decode_With_String_Valid_Polyline_Returns_Expected_Coordinates() { // Arrange - TestMemoryDecoder decoder = new(); + var decoder = CreateMemoryDecoder(); string polyline = StaticValueProvider.Valid.GetPolyline(); (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; diff --git a/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs index da8a622e..21aea09b 100644 --- a/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs +++ b/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs @@ -5,6 +5,7 @@ namespace PolylineAlgorithm.Tests.Extensions; +using PolylineAlgorithm; using PolylineAlgorithm.Abstraction; using PolylineAlgorithm.Extensions; using PolylineAlgorithm.Utility; @@ -16,10 +17,16 @@ namespace PolylineAlgorithm.Tests.Extensions; /// [TestClass] public sealed class PolylineEncoderExtensionsTests { - private sealed class TestStringEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string> { - protected override string CreatePolyline(ReadOnlyMemory polyline) => polyline.ToString(); - protected override double GetLatitude((double Latitude, double Longitude) current) => current.Latitude; - protected override double GetLongitude((double Latitude, double Longitude) current) => current.Longitude; + private static PolylineEncoder<(double Latitude, double Longitude), string> CreateTestEncoder() { + PolylineFormatter<(double Latitude, double Longitude), string> formatter = + FormatterBuilder<(double Latitude, double Longitude), string>.Create() + .AddValue("lat", static c => c.Latitude) + .AddValue("lon", static c => c.Longitude) + .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .Build(); + + return new PolylineEncoder<(double Latitude, double Longitude), string>( + new PolylineOptions<(double Latitude, double Longitude), string>(formatter)); } // ----- Encode(List) ----- @@ -45,7 +52,7 @@ public void Encode_With_List_Null_Encoder_Throws_ArgumentNullException() { [TestMethod] public void Encode_With_List_Null_Coordinates_Throws_ArgumentNullException() { // Arrange - TestStringEncoder encoder = new(); + var encoder = CreateTestEncoder(); List<(double, double)>? coordinates = null; // Act & Assert @@ -60,7 +67,7 @@ public void Encode_With_List_Null_Coordinates_Throws_ArgumentNullException() { [TestMethod] public void Encode_With_List_Valid_Coordinates_Returns_Expected_Polyline() { // Arrange - TestStringEncoder encoder = new(); + var encoder = CreateTestEncoder(); List<(double Latitude, double Longitude)> coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; string expected = StaticValueProvider.Valid.GetPolyline(); @@ -95,7 +102,7 @@ public void Encode_With_Array_Null_Encoder_Throws_ArgumentNullException() { [TestMethod] public void Encode_With_Array_Null_Coordinates_Throws_ArgumentNullException() { // Arrange — call the extension method explicitly (same reasoning as above). - IPolylineEncoder<(double, double), string> encoder = new TestStringEncoder(); + IPolylineEncoder<(double, double), string> encoder = CreateTestEncoder(); (double, double)[]? coordinates = null; // Act & Assert @@ -110,7 +117,7 @@ public void Encode_With_Array_Null_Coordinates_Throws_ArgumentNullException() { [TestMethod] public void Encode_With_Array_Valid_Coordinates_Returns_Expected_Polyline() { // Arrange - TestStringEncoder encoder = new(); + var encoder = CreateTestEncoder(); (double Latitude, double Longitude)[] coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; string expected = StaticValueProvider.Valid.GetPolyline(); diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs index 26dafc74..de5cdd06 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs @@ -5,421 +5,194 @@ namespace PolylineAlgorithm.Tests; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using PolylineAlgorithm; using System; /// -/// Tests for . +/// Tests for , +/// , and +/// write/read/create delegation. /// [TestClass] public sealed class PolylineEncodingOptionsBuilderTests { - /// - /// Tests that Create returns a new builder instance. - /// - [TestMethod] - public void Create_Returns_New_Builder() { - // Act - PolylineEncodingOptionsBuilder result = PolylineEncodingOptionsBuilder.Create(); + private static readonly Func, string> _write = m => new string(m.Span); + private static readonly Func> _read = s => s.AsMemory(); - // Assert - Assert.IsNotNull(result); - } + // --------------------------------------------------------------------------- + // FormatterBuilder.ForPolyline — argument validation + // --------------------------------------------------------------------------- - /// - /// Tests that Create returns different instances on multiple calls. - /// + /// Tests that ForPolyline with a null write delegate throws . [TestMethod] - public void Create_With_Multiple_Invocations_Returns_Different_Instances() { - // Act - PolylineEncodingOptionsBuilder first = PolylineEncodingOptionsBuilder.Create(); - PolylineEncodingOptionsBuilder second = PolylineEncodingOptionsBuilder.Create(); - - // Assert - Assert.AreNotSame(first, second); - } - - /// - /// Tests that Build returns options with default values. - /// - [TestMethod] - public void Build_With_Defaults_Returns_Options_With_Default_Values() { + public void ForPolyline_With_Null_Write_Throws_ArgumentNullException() { // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - PolylineEncodingOptions result = builder.Build(); + FormatterBuilder builder = FormatterBuilder.Create() + .AddValue("Value", static v => v); - // Assert - Assert.IsNotNull(result); - Assert.AreEqual(5u, result.Precision); - Assert.AreEqual(512, result.StackAllocLimit); - Assert.IsNotNull(result.LoggerFactory); - Assert.IsInstanceOfType(result.LoggerFactory); + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => builder.ForPolyline(null!, _read)); + Assert.AreEqual("write", ex.ParamName); } - /// - /// Tests that Build returns options with configured precision. - /// + /// Tests that ForPolyline with a null read delegate throws . [TestMethod] - public void Build_With_Custom_Precision_Returns_Options_With_Custom_Precision() { + public void ForPolyline_With_Null_Read_Throws_ArgumentNullException() { // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create() - .WithPrecision(7); - - // Act - PolylineEncodingOptions result = builder.Build(); + FormatterBuilder builder = FormatterBuilder.Create() + .AddValue("Value", static v => v); - // Assert - Assert.AreEqual(7u, result.Precision); + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => builder.ForPolyline(_write, null!)); + Assert.AreEqual("read", ex.ParamName); } - /// - /// Tests that Build returns options with configured stack alloc limit. - /// + /// Tests that ForPolyline returns the same builder instance for method chaining. [TestMethod] - public void Build_With_Custom_Stack_Alloc_Limit_Returns_Options_With_Custom_Stack_Alloc_Limit() { + public void ForPolyline_Returns_Same_Builder_For_Method_Chaining() { // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create() - .WithStackAllocLimit(1024); + FormatterBuilder builder = FormatterBuilder.Create() + .AddValue("Value", static v => v); // Act - PolylineEncodingOptions result = builder.Build(); + FormatterBuilder result = builder.ForPolyline(_write, _read); // Assert - Assert.AreEqual(1024, result.StackAllocLimit); + Assert.AreSame(builder, result); } - /// - /// Tests that Build returns options with configured logger factory. - /// + /// Tests that Build without a prior ForPolyline call throws . [TestMethod] - public void Build_With_Custom_Logger_Factory_Returns_Options_With_Custom_Logger_Factory() { + public void Build_Without_ForPolyline_Throws_InvalidOperationException() { // Arrange - ILoggerFactory loggerFactory = LoggerFactory.Create(_ => { }); - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create() - .WithLoggerFactory(loggerFactory); - - // Act - PolylineEncodingOptions result = builder.Build(); + FormatterBuilder builder = FormatterBuilder.Create() + .AddValue("Value", static v => v); - // Assert - Assert.AreSame(loggerFactory, result.LoggerFactory); - - // Cleanup - loggerFactory.Dispose(); + // Act & Assert + Assert.ThrowsExactly(() => builder.Build()); } - /// - /// Tests that Build returns options with all custom values. - /// - [TestMethod] - public void Build_With_All_Custom_Values_Returns_Options_With_All_Custom_Values() { - // Arrange - ILoggerFactory loggerFactory = LoggerFactory.Create(_ => { }); - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create() - .WithPrecision(10) - .WithStackAllocLimit(2048) - .WithLoggerFactory(loggerFactory); - - // Act - PolylineEncodingOptions result = builder.Build(); - - // Assert - Assert.AreEqual(10u, result.Precision); - Assert.AreEqual(2048, result.StackAllocLimit); - Assert.AreSame(loggerFactory, result.LoggerFactory); - - // Cleanup - loggerFactory.Dispose(); - } + // --------------------------------------------------------------------------- + // FormatterBuilder.WithCreate — argument validation + // --------------------------------------------------------------------------- - /// - /// Tests that Build can be called multiple times on the same builder. - /// + /// Tests that WithCreate with a null factory throws . [TestMethod] - public void Build_With_Multiple_Invocations_Returns_Different_Instances_With_Same_Values() { + public void WithCreate_With_Null_Factory_Throws_ArgumentNullException() { // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create() - .WithPrecision(6); + FormatterBuilder builder = FormatterBuilder.Create() + .AddValue("Value", static v => v); - // Act - PolylineEncodingOptions first = builder.Build(); - PolylineEncodingOptions second = builder.Build(); - - // Assert - Assert.AreNotSame(first, second); - Assert.AreEqual(first.Precision, second.Precision); - Assert.AreEqual(first.StackAllocLimit, second.StackAllocLimit); + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => builder.WithCreate(null!)); + Assert.AreEqual("create", ex.ParamName); } - /// - /// Tests that WithStackAllocLimit sets the value and returns the builder. - /// + /// Tests that WithCreate returns the same builder instance for method chaining. [TestMethod] - public void WithStackAllocLimit_With_Valid_Value_Sets_Value_And_Returns_Self() { + public void WithCreate_Returns_Same_Builder_For_Method_Chaining() { // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); + FormatterBuilder builder = FormatterBuilder.Create() + .AddValue("Value", static v => v); // Act - PolylineEncodingOptionsBuilder result = builder.WithStackAllocLimit(256); + FormatterBuilder result = builder.WithCreate(static v => v[0] / 1e5); // Assert Assert.AreSame(builder, result); - PolylineEncodingOptions options = builder.Build(); - Assert.AreEqual(256, options.StackAllocLimit); - } - - /// - /// Tests that WithStackAllocLimit accepts minimum value of 1. - /// - [TestMethod] - public void WithStackAllocLimit_With_Minimum_Value_Sets_Value() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - builder.WithStackAllocLimit(1); - PolylineEncodingOptions result = builder.Build(); - - // Assert - Assert.AreEqual(1, result.StackAllocLimit); - } - - /// - /// Tests that WithStackAllocLimit throws ArgumentOutOfRangeException for zero. - /// - [TestMethod] - public void WithStackAllocLimit_With_Zero_Throws_ArgumentOutOfRangeException() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - var exception = Assert.ThrowsExactly(() => builder.WithStackAllocLimit(0)); - - // Assert - Assert.AreEqual("stackAllocLimit", exception.ParamName); } - /// - /// Tests that WithStackAllocLimit throws ArgumentOutOfRangeException for negative value. - /// - [TestMethod] - public void WithStackAllocLimit_With_Negative_Value_Throws_ArgumentOutOfRangeException() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - var exception = Assert.ThrowsExactly(() => builder.WithStackAllocLimit(-10)); + // --------------------------------------------------------------------------- + // PolylineFormatter — Write and Read delegation + // --------------------------------------------------------------------------- - // Assert - Assert.AreEqual("stackAllocLimit", exception.ParamName); - } - - /// - /// Tests that WithStackAllocLimit accepts large value. - /// + /// Tests that Write delegates to the supplied write function. [TestMethod] - public void WithStackAllocLimit_With_Large_Value_Sets_Value() { + public void Write_Delegates_To_Supplied_Write_Function() { // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - PolylineEncodingOptions result = builder - .WithStackAllocLimit(100000) + ReadOnlyMemory input = "hello".AsMemory(); + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); - // Assert - Assert.AreEqual(100000, result.StackAllocLimit); - } - - /// - /// Tests that WithStackAllocLimit can be called multiple times. - /// - [TestMethod] - public void WithStackAllocLimit_With_Multiple_Calls_Last_Value_Wins() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - // Act - PolylineEncodingOptions result = builder.WithStackAllocLimit(100) - .WithStackAllocLimit(200) - .WithStackAllocLimit(300) - .Build(); + string result = formatter.Write(input); // Assert - Assert.AreEqual(300, result.StackAllocLimit); - } - - /// - /// Tests that WithPrecision sets the value and returns the builder. - /// - [TestMethod] - public void WithPrecision_With_Valid_Value_Sets_Value_And_Returns_Self() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - PolylineEncodingOptionsBuilder result = builder - .WithPrecision(8); - PolylineEncodingOptions options = builder.Build(); - - // Assert - Assert.AreSame(builder, result); - Assert.AreEqual(8u, options.Precision); + Assert.AreEqual("hello", result); } - /// - /// Tests that WithPrecision accepts zero value. - /// + /// Tests that Read delegates to the supplied read function. [TestMethod] - public void WithPrecision_With_Zero_Sets_Value() { + public void Read_Delegates_To_Supplied_Read_Function() { // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - PolylineEncodingOptions result = builder - .WithPrecision(0) + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); - // Assert - Assert.AreEqual(0u, result.Precision); - } - - /// - /// Tests that WithPrecision accepts maximum uint value. - /// - [TestMethod] - public void WithPrecision_With_Max_Value_Sets_Value() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - // Act - PolylineEncodingOptions result = builder - .WithPrecision(uint.MaxValue) - .Build(); + ReadOnlyMemory result = formatter.Read("world"); // Assert - Assert.AreEqual(uint.MaxValue, result.Precision); + Assert.IsTrue("world".AsMemory().Span.SequenceEqual(result.Span)); } - /// - /// Tests that WithPrecision can be called multiple times. - /// - [TestMethod] - public void WithPrecision_With_Multiple_Calls_Last_Value_Wins() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - PolylineEncodingOptions result = builder - .WithPrecision(5) - .WithPrecision(7) - .WithPrecision(9) - .Build(); + // --------------------------------------------------------------------------- + // PolylineFormatter — CreateItem + // --------------------------------------------------------------------------- - // Assert - Assert.AreEqual(9u, result.Precision); - } - - /// - /// Tests that WithLoggerFactory sets the factory and returns the builder. - /// - [TestMethod] - public void WithLoggerFactory_With_Valid_Factory_Sets_Value_And_Returns_Self() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - ILoggerFactory loggerFactory = LoggerFactory.Create(_ => { }); - - // Act - PolylineEncodingOptionsBuilder result = builder - .WithLoggerFactory(loggerFactory); - PolylineEncodingOptions options = builder.Build(); - - // Assert - Assert.AreSame(builder, result); - Assert.AreSame(loggerFactory, options.LoggerFactory); - - // Cleanup - loggerFactory.Dispose(); - } - - /// - /// Tests that WithLoggerFactory with null uses NullLoggerFactory. - /// + /// Tests that CreateItem without a factory throws . [TestMethod] - public void WithLoggerFactory_With_Null_Uses_Null_LoggerFactory() { - // Arrange - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create(); - - // Act - PolylineEncodingOptions result = builder - .WithLoggerFactory(null!) + public void CreateItem_Without_Factory_Throws_InvalidOperationException() { + // Arrange — no WithCreate call + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); - // Assert - Assert.IsNotNull(result.LoggerFactory); - Assert.IsInstanceOfType(result.LoggerFactory); + // Act & Assert + Assert.ThrowsExactly( + () => formatter.CreateItem(new long[] { 100000L }.AsSpan())); } - /// - /// Tests that WithLoggerFactory can replace a previously set factory. - /// + /// Tests that CreateItem with a factory correctly constructs the item. [TestMethod] - public void WithLoggerFactory_With_Replace_Previous_Factory_Updates_Value() { - // Arrange - using ILoggerFactory firstFactory = LoggerFactory.Create(_ => { }); - using ILoggerFactory secondFactory = LoggerFactory.Create(_ => { }); - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create() - .WithLoggerFactory(firstFactory); - - // Act - PolylineEncodingOptions result = builder - .WithLoggerFactory(secondFactory) + public void CreateItem_With_Factory_Returns_Expected_Value() { + // Arrange — precision 5, factory divides by 1e5 + PolylineFormatter formatter = FormatterBuilder.Create() + .AddValue("Value", static v => v, precision: 5) + .WithCreate(static v => v[0] / 1e5) + .ForPolyline(_write, _read) .Build(); - // Assert - Assert.AreSame(secondFactory, result.LoggerFactory); - } - - /// - /// Tests that WithLoggerFactory can be set to null after setting a factory. - /// - [TestMethod] - public void WithLoggerFactory_With_Null_After_Factory_Uses_Null_LoggerFactory() { - // Arrange - using ILoggerFactory factory = LoggerFactory.Create(_ => { }); - PolylineEncodingOptionsBuilder builder = PolylineEncodingOptionsBuilder.Create() - .WithLoggerFactory(factory); - - // Act - builder.WithLoggerFactory(null!); - PolylineEncodingOptions result = builder.Build(); + // Act — 3850000 / 100000.0 = 38.5 + double result = formatter.CreateItem(new long[] { 3850000L }.AsSpan()); // Assert - Assert.IsInstanceOfType(result.LoggerFactory); + Assert.AreEqual(38.5, result, 1e-9); } - /// - /// Tests that builder supports method chaining for all methods. - /// + /// Tests that CreateItem with a multi-value factory returns the correct item. [TestMethod] - public void MethodChaining_With_All_Methods_Returns_Builder_For_Chaining() { + public void CreateItem_With_Multi_Value_Factory_Returns_Expected_Tuple() { // Arrange - using ILoggerFactory loggerFactory = LoggerFactory.Create(_ => { }); + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("Lat", static t => t.Lat, precision: 5) + .AddValue("Lon", static t => t.Lon, precision: 5) + .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .ForPolyline(_write, _read) + .Build(); // Act - PolylineEncodingOptions result = PolylineEncodingOptionsBuilder.Create() - .WithPrecision(6) - .WithStackAllocLimit(1024) - .WithLoggerFactory(loggerFactory) - .Build(); + (double Lat, double Lon) result = formatter.CreateItem(new long[] { 3850000L, -12025000L }.AsSpan()); // Assert - Assert.IsNotNull(result); - Assert.AreEqual(6u, result.Precision); - Assert.AreEqual(1024, result.StackAllocLimit); - Assert.AreSame(loggerFactory, result.LoggerFactory); + Assert.AreEqual(38.5, result.Lat, 1e-9); + Assert.AreEqual(-120.25, result.Lon, 1e-9); } -} \ No newline at end of file +} diff --git a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs index 537c48fc..cd72b29c 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs @@ -5,6 +5,8 @@ namespace PolylineAlgorithm.Tests; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using PolylineAlgorithm; using System; @@ -18,13 +20,13 @@ public sealed class PolylineFormatterTests { private static readonly Func, string> _write = m => new string(m.Span); private static readonly Func> _read = s => s.AsMemory(); // --------------------------------------------------------------------------- - // FormatterBuilder.Create + // FormatterBuilder.Create // --------------------------------------------------------------------------- [TestMethod] public void Create_Returns_New_Builder() { // Act - FormatterBuilder<(double X, double Y)> result = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> result = FormatterBuilder<(double X, double Y), string>.Create(); // Assert Assert.IsNotNull(result); @@ -33,21 +35,21 @@ public void Create_Returns_New_Builder() { [TestMethod] public void Create_With_Multiple_Invocations_Returns_Different_Instances() { // Act - FormatterBuilder<(double X, double Y)> first = FormatterBuilder<(double X, double Y)>.Create(); - FormatterBuilder<(double X, double Y)> second = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> first = FormatterBuilder<(double X, double Y), string>.Create(); + FormatterBuilder<(double X, double Y), string> second = FormatterBuilder<(double X, double Y), string>.Create(); // Assert Assert.AreNotSame(first, second); } // --------------------------------------------------------------------------- - // FormatterBuilder.AddValue — argument validation + // FormatterBuilder.AddValue — argument validation // --------------------------------------------------------------------------- [TestMethod] public void AddValue_With_Null_Name_Throws_ArgumentNullException() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create(); // Act & Assert ArgumentNullException ex = Assert.ThrowsExactly( @@ -58,7 +60,7 @@ public void AddValue_With_Null_Name_Throws_ArgumentNullException() { [TestMethod] public void AddValue_With_Empty_Name_Throws_ArgumentException() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create(); // Act & Assert ArgumentException ex = Assert.ThrowsExactly( @@ -69,7 +71,7 @@ public void AddValue_With_Empty_Name_Throws_ArgumentException() { [TestMethod] public void AddValue_With_Null_Selector_Throws_ArgumentNullException() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create(); // Act & Assert ArgumentNullException ex = Assert.ThrowsExactly( @@ -80,7 +82,7 @@ public void AddValue_With_Null_Selector_Throws_ArgumentNullException() { [TestMethod] public void AddValue_With_Duplicate_Name_Throws_ArgumentException() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create(); builder.AddValue("X", static t => t.X); // Act & Assert @@ -90,16 +92,16 @@ public void AddValue_With_Duplicate_Name_Throws_ArgumentException() { } // --------------------------------------------------------------------------- - // FormatterBuilder.AddValue — happy path & chaining + // FormatterBuilder.AddValue — happy path & chaining // --------------------------------------------------------------------------- [TestMethod] public void AddValue_Returns_Same_Builder_For_Method_Chaining() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create(); // Act - FormatterBuilder<(double X, double Y)> result = builder.AddValue("X", static t => t.X); + FormatterBuilder<(double X, double Y), string> result = builder.AddValue("X", static t => t.X); // Assert Assert.AreSame(builder, result); @@ -108,9 +110,10 @@ public void AddValue_Returns_Same_Builder_For_Method_Chaining() { [TestMethod] public void AddValue_With_Different_Names_Succeeds() { // Arrange & Act - PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineFormatter<(double X, double Y), string> formatter = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) + .ForPolyline(_write, _read) .Build(); // Assert @@ -118,30 +121,30 @@ public void AddValue_With_Different_Names_Succeeds() { } // --------------------------------------------------------------------------- - // FormatterBuilder.SetBaseline — argument validation + // FormatterBuilder.SetBaseline — argument validation // --------------------------------------------------------------------------- [TestMethod] public void SetBaseline_With_No_Rules_Throws_InvalidOperationException() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create(); // Act & Assert Assert.ThrowsExactly(() => builder.SetBaseline(1000L)); } // --------------------------------------------------------------------------- - // FormatterBuilder.SetBaseline — happy path + // FormatterBuilder.SetBaseline — happy path // --------------------------------------------------------------------------- [TestMethod] public void SetBaseline_Returns_Same_Builder_For_Method_Chaining() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create() + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X); // Act - FormatterBuilder<(double X, double Y)> result = builder.SetBaseline(100L); + FormatterBuilder<(double X, double Y), string> result = builder.SetBaseline(100L); // Assert Assert.AreSame(builder, result); @@ -150,10 +153,11 @@ public void SetBaseline_Returns_Same_Builder_For_Method_Chaining() { [TestMethod] public void SetBaseline_Applies_Only_To_Last_Added_Rule() { // Arrange & Act - PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineFormatter<(double X, double Y), string> formatter = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .SetBaseline(500L) + .ForPolyline(_write, _read) .Build(); // Assert — only Y (index 1) has a baseline; X (index 0) returns 0 @@ -164,9 +168,10 @@ public void SetBaseline_Applies_Only_To_Last_Added_Rule() { [TestMethod] public void SetBaseline_Can_Be_Called_On_Each_Rule() { // Arrange & Act - PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineFormatter<(double X, double Y), string> formatter = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X).SetBaseline(100L) .AddValue("Y", static t => t.Y).SetBaseline(200L) + .ForPolyline(_write, _read) .Build(); // Assert @@ -175,13 +180,13 @@ public void SetBaseline_Can_Be_Called_On_Each_Rule() { } // --------------------------------------------------------------------------- - // FormatterBuilder.Build — validation + // FormatterBuilder.Build — validation // --------------------------------------------------------------------------- [TestMethod] public void Build_With_No_Rules_Throws_InvalidOperationException() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create(); + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create(); // Act & Assert Assert.ThrowsExactly(() => builder.Build()); @@ -190,28 +195,30 @@ public void Build_With_No_Rules_Throws_InvalidOperationException() { [TestMethod] public void Build_With_Multiple_Invocations_Returns_Different_Instances() { // Arrange - FormatterBuilder<(double X, double Y)> builder = FormatterBuilder<(double X, double Y)>.Create() - .AddValue("X", static t => t.X); + FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create() + .AddValue("X", static t => t.X) + .ForPolyline(_write, _read); // Act - PolylineValueFormatter<(double X, double Y)> first = builder.Build(); - PolylineValueFormatter<(double X, double Y)> second = builder.Build(); + PolylineFormatter<(double X, double Y), string> first = builder.Build(); + PolylineFormatter<(double X, double Y), string> second = builder.Build(); // Assert Assert.AreNotSame(first, second); } // --------------------------------------------------------------------------- - // PolylineValueFormatter.Width + // PolylineFormatter.Width // --------------------------------------------------------------------------- [TestMethod] public void Width_Equals_Number_Of_Added_Rules() { // Arrange & Act - PolylineValueFormatter<(double X, double Y, double Z)> formatter = FormatterBuilder<(double X, double Y, double Z)>.Create() + PolylineFormatter<(double X, double Y, double Z), string> formatter = FormatterBuilder<(double X, double Y, double Z), string>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .AddValue("Z", static t => t.Z) + .ForPolyline(_write, _read) .Build(); // Assert @@ -221,8 +228,9 @@ public void Width_Equals_Number_Of_Added_Rules() { [TestMethod] public void Width_Is_One_For_Single_Rule() { // Arrange & Act - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); // Assert @@ -230,54 +238,15 @@ public void Width_Is_One_For_Single_Rule() { } // --------------------------------------------------------------------------- - // PolylineValueFormatter.HasBaselines - // --------------------------------------------------------------------------- - - [TestMethod] - public void HasBaselines_Is_False_When_No_Baselines_Are_Set() { - // Arrange & Act - PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() - .AddValue("X", static t => t.X) - .AddValue("Y", static t => t.Y) - .Build(); - - // Assert - Assert.IsFalse(formatter.HasBaselines); - } - - [TestMethod] - public void HasBaselines_Is_True_When_Any_Baseline_Is_Set() { - // Arrange & Act - PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() - .AddValue("X", static t => t.X) - .AddValue("Y", static t => t.Y).SetBaseline(100L) - .Build(); - - // Assert - Assert.IsTrue(formatter.HasBaselines); - } - - [TestMethod] - public void HasBaselines_Is_True_When_All_Baselines_Are_Set() { - // Arrange & Act - PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() - .AddValue("X", static t => t.X).SetBaseline(10L) - .AddValue("Y", static t => t.Y).SetBaseline(20L) - .Build(); - - // Assert - Assert.IsTrue(formatter.HasBaselines); - } - - // --------------------------------------------------------------------------- - // PolylineValueFormatter.GetBaseline + // PolylineFormatter.GetBaseline // --------------------------------------------------------------------------- [TestMethod] public void GetBaseline_Returns_Zero_When_No_Baseline_Configured() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); // Act @@ -290,9 +259,10 @@ public void GetBaseline_Returns_Zero_When_No_Baseline_Configured() { [TestMethod] public void GetBaseline_Returns_Configured_Baseline() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .SetBaseline(42L) + .ForPolyline(_write, _read) .Build(); // Act @@ -305,9 +275,10 @@ public void GetBaseline_Returns_Configured_Baseline() { [TestMethod] public void GetBaseline_Returns_Negative_Baseline() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .SetBaseline(-1000L) + .ForPolyline(_write, _read) .Build(); // Act @@ -318,14 +289,15 @@ public void GetBaseline_Returns_Negative_Baseline() { } // --------------------------------------------------------------------------- - // PolylineValueFormatter.GetValues + // PolylineFormatter.GetValues // --------------------------------------------------------------------------- [TestMethod] public void GetValues_Scales_Single_Column_By_Factor() { // Arrange — precision 5 → factor = 100000; use a value exact in double arithmetic - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) + .ForPolyline(_write, _read) .Build(); Span output = stackalloc long[1]; @@ -340,10 +312,11 @@ public void GetValues_Scales_Single_Column_By_Factor() { [TestMethod] public void GetValues_Scales_Multiple_Columns_Independently() { // Arrange - PolylineValueFormatter<(double Lat, double Lon)> formatter = - FormatterBuilder<(double Lat, double Lon)>.Create() + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("Lat", static t => t.Lat, precision: 5) .AddValue("Lon", static t => t.Lon, precision: 5) + .ForPolyline(_write, _read) .Build(); Span output = stackalloc long[2]; @@ -359,9 +332,10 @@ public void GetValues_Scales_Multiple_Columns_Independently() { [TestMethod] public void GetValues_With_Wrong_Buffer_Length_Throws_ArgumentException() { // Arrange — formatter has Width = 2 but buffer has length 1 - PolylineValueFormatter<(double X, double Y)> formatter = FormatterBuilder<(double X, double Y)>.Create() + PolylineFormatter<(double X, double Y), string> formatter = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) + .ForPolyline(_write, _read) .Build(); long[] tooShort = new long[1]; @@ -375,8 +349,9 @@ public void GetValues_With_Wrong_Buffer_Length_Throws_ArgumentException() { [TestMethod] public void GetValues_With_Oversized_Buffer_Throws_ArgumentException() { // Arrange — formatter has Width = 1 but buffer has length 3 - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); long[] tooLong = new long[3]; @@ -390,8 +365,9 @@ public void GetValues_With_Oversized_Buffer_Throws_ArgumentException() { [TestMethod] public void GetValues_With_Zero_Returns_Zero() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) + .ForPolyline(_write, _read) .Build(); Span output = stackalloc long[1]; @@ -406,8 +382,9 @@ public void GetValues_With_Zero_Returns_Zero() { [TestMethod] public void GetValues_With_Negative_Value_Returns_Negative_Scaled_Long() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) + .ForPolyline(_write, _read) .Build(); Span output = stackalloc long[1]; @@ -422,8 +399,9 @@ public void GetValues_With_Negative_Value_Returns_Negative_Scaled_Long() { [TestMethod] public void GetValues_With_Custom_Precision_Scales_Correctly() { // Arrange — precision 3 → factor = 1000 - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 3) + .ForPolyline(_write, _read) .Build(); Span output = stackalloc long[1]; @@ -436,95 +414,95 @@ public void GetValues_With_Custom_Precision_Scales_Correctly() { } // --------------------------------------------------------------------------- - // PolylineOptions constructor validation + // PolylineOptions constructor validation // --------------------------------------------------------------------------- [TestMethod] - public void PolylineOptions_With_Null_ValueFormatter_Throws_ArgumentNullException() { + public void PolylineOptions_With_Null_Formatter_Throws_ArgumentNullException() { // Act & Assert ArgumentNullException ex = Assert.ThrowsExactly( - () => _ = new PolylineOptions(null!, PolylineFormatter.ForString)); - Assert.AreEqual("valueFormatter", ex.ParamName); + () => _ = new PolylineOptions(null!)); + Assert.AreEqual("formatter", ex.ParamName); } + // --------------------------------------------------------------------------- + // PolylineOptions properties + // --------------------------------------------------------------------------- + [TestMethod] - public void PolylineOptions_With_Null_PolylineFormatter_Throws_ArgumentNullException() { + public void PolylineOptions_Stores_Formatter() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => _ = new PolylineOptions(formatter, null!)); - Assert.AreEqual("polylineFormatter", ex.ParamName); - } + // Act + PolylineOptions options = new(formatter); - // --------------------------------------------------------------------------- - // PolylineOptions properties - // --------------------------------------------------------------------------- + // Assert + Assert.AreSame(formatter, options.Formatter); + } [TestMethod] - public void PolylineOptions_Stores_ValueFormatter() { + public void PolylineOptions_Default_StackAllocLimit_Is_512() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); // Act - PolylineOptions options = new(formatter, PolylineFormatter.ForString); + PolylineOptions options = new(formatter); // Assert - Assert.AreSame(formatter, options.ValueFormatter); + Assert.AreEqual(512, options.StackAllocLimit); } [TestMethod] - public void PolylineOptions_Stores_PolylineFormatter() { + public void PolylineOptions_Stores_Custom_StackAllocLimit() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); // Act - PolylineOptions options = new(formatter, PolylineFormatter.ForString); + PolylineOptions options = new(formatter, stackAllocLimit: 1024); // Assert - Assert.AreSame(PolylineFormatter.ForString, options.PolylineFormatter); + Assert.AreEqual(1024, options.StackAllocLimit); } [TestMethod] - public void PolylineOptions_With_Null_Encoding_Uses_Default_Options() { + public void PolylineOptions_Default_LoggerFactory_Is_NullLoggerFactory() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); // Act - PolylineOptions options = new(formatter, PolylineFormatter.ForString, null); + PolylineOptions options = new(formatter); // Assert - Assert.IsNotNull(options.Encoding); - Assert.AreEqual(5u, options.Encoding.Precision); - Assert.AreEqual(512, options.Encoding.StackAllocLimit); + Assert.IsInstanceOfType(options.LoggerFactory); } [TestMethod] - public void PolylineOptions_Stores_Custom_Encoding_Options() { + public void PolylineOptions_Stores_Custom_LoggerFactory() { // Arrange - PolylineValueFormatter formatter = FormatterBuilder.Create() + PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) + .ForPolyline(_write, _read) .Build(); - PolylineEncodingOptions encoding = PolylineEncodingOptionsBuilder.Create() - .WithPrecision(7) - .WithStackAllocLimit(1024) - .Build(); + + using ILoggerFactory loggerFactory = LoggerFactory.Create(_ => { }); // Act - PolylineOptions options = new(formatter, PolylineFormatter.ForString, encoding); + PolylineOptions options = new(formatter, loggerFactory: loggerFactory); // Assert - Assert.AreSame(encoding, options.Encoding); - Assert.AreEqual(7u, options.Encoding.Precision); - Assert.AreEqual(1024, options.Encoding.StackAllocLimit); + Assert.AreSame(loggerFactory, options.LoggerFactory); } } diff --git a/utilities/PolylineAlgorithm.Utility/RandomValueProvider.cs b/utilities/PolylineAlgorithm.Utility/RandomValueProvider.cs index d3d83b9e..e17c7852 100644 --- a/utilities/PolylineAlgorithm.Utility/RandomValueProvider.cs +++ b/utilities/PolylineAlgorithm.Utility/RandomValueProvider.cs @@ -5,7 +5,6 @@ namespace PolylineAlgorithm.Utility; -using PolylineAlgorithm.Abstraction; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -20,7 +19,19 @@ namespace PolylineAlgorithm.Utility; internal static class RandomValueProvider { private static readonly Random _random = new(DateTime.Now.Millisecond); private static readonly ConcurrentDictionary _cache = new(); - private static readonly PolylineEncoder _encoder = new(); + private static readonly PolylineEncoder<(double Latitude, double Longitude), string> _encoder = CreateEncoder(); + + private static PolylineEncoder<(double Latitude, double Longitude), string> CreateEncoder() { + PolylineFormatter<(double Latitude, double Longitude), string> formatter = + FormatterBuilder<(double Latitude, double Longitude), string>.Create() + .AddValue("lat", static c => c.Latitude) + .AddValue("lon", static c => c.Longitude) + .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .Build(); + + return new PolylineEncoder<(double Latitude, double Longitude), string>( + new PolylineOptions<(double Latitude, double Longitude), string>(formatter)); + } /// /// Gets a collection of random latitude/longitude tuples of the specified count. @@ -103,18 +114,4 @@ private readonly struct PolylineCoordinateCollectionPair(IEnumerable<(double Lat public string Polyline { get; } = polyline; } - private sealed class PolylineEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string> { - - protected override string CreatePolyline(ReadOnlyMemory polyline) { - return polyline.ToString(); - } - - protected override double GetLatitude((double Latitude, double Longitude) current) { - return current.Latitude; - } - - protected override double GetLongitude((double Latitude, double Longitude) current) { - return current.Longitude; - } - } } \ No newline at end of file From b4e6d43189bf47193bb98810157d8bd2fed13459 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:26:35 +0000 Subject: [PATCH 11/22] fix: WithCreate receives denormalized doubles; replace sample wrapper classes with direct PolylineEncoder/Decoder usage Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/76e69a37-a3c2-450f-8078-46133933fc61 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../PolylineDecoderBenchmark.cs | 2 +- .../NetTopologyPolylineDecoder.cs | 42 ------ .../NetTopologyPolylineEncoder.cs | 39 ----- .../Program.cs | 22 ++- .../Program.cs | 27 +++- .../SensorDataDecoder.cs | 107 -------------- .../SensorDataEncoder.cs | 135 ------------------ .../SensorEncodingOptions.cs | 39 ----- .../Abstraction/IPolylineFormatter.cs | 11 +- src/PolylineAlgorithm/FormatterBuilder.cs | 10 +- src/PolylineAlgorithm/PolylineFormatter.cs | 12 +- src/PolylineAlgorithm/PolylineItemFactory.cs | 12 +- src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 2 +- .../AbstractPolylineDecoderTests.cs | 2 +- .../AbstractPolylineEncoderTests.cs | 2 +- .../PolylineDecoderExtensionsTests.cs | 4 +- .../PolylineEncodingOptionsBuilderTests.cs | 11 +- 17 files changed, 85 insertions(+), 394 deletions(-) delete mode 100644 samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs delete mode 100644 samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs delete mode 100644 samples/PolylineAlgorithm.SensorData.Sample/SensorDataDecoder.cs delete mode 100644 samples/PolylineAlgorithm.SensorData.Sample/SensorDataEncoder.cs delete mode 100644 samples/PolylineAlgorithm.SensorData.Sample/SensorEncodingOptions.cs diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs index 0195da78..e6a1ed76 100644 --- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs +++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs @@ -57,7 +57,7 @@ public class PolylineDecoderBenchmark { FormatterBuilder<(double Latitude, double Longitude), T>.Create() .AddValue("lat", static c => c.Latitude) .AddValue("lon", static c => c.Longitude) - .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .WithCreate(static v => (v[0], v[1])) .ForPolyline(write, read) .Build(); diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs deleted file mode 100644 index 408f44e4..00000000 --- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineDecoder.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.NetTopologySuite.Sample; - -using global::NetTopologySuite.Geometries; -using PolylineAlgorithm; -using PolylineAlgorithm.Abstraction; -using System.Collections.Generic; -using System.Threading; - -/// -/// Polyline decoder using NetTopologySuite. -/// -internal sealed class NetTopologyPolylineDecoder : IPolylineDecoder { - private readonly PolylineDecoder _inner; - - /// - /// Initializes a new instance of the class. - /// - internal NetTopologyPolylineDecoder() { - PolylineFormatter formatter = - FormatterBuilder.Create() - // NetTopologySuite Point: Y = latitude, X = longitude - .AddValue("lat", static p => p.Y) - .AddValue("lon", static p => p.X) - // v[0] = scaled latitude, v[1] = scaled longitude (factor = 1e5 for default precision 5) - .WithCreate(static v => new Point(x: v[1] / 1e5, y: v[0] / 1e5)) - .ForPolyline( - static m => m.IsEmpty ? string.Empty : new string(m.Span), - static s => s.AsMemory()) - .Build(); - - _inner = new PolylineDecoder(new PolylineOptions(formatter)); - } - - /// - public IEnumerable Decode(string polyline, CancellationToken cancellationToken = default) - => _inner.Decode(polyline, cancellationToken); -} \ No newline at end of file diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs deleted file mode 100644 index 7eb0892b..00000000 --- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/NetTopologyPolylineEncoder.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.NetTopologySuite.Sample; - -using global::NetTopologySuite.Geometries; -using PolylineAlgorithm; -using PolylineAlgorithm.Abstraction; -using System.Threading; - -/// -/// Polyline encoder using NetTopologySuite's Point type. -/// -internal sealed class NetTopologyPolylineEncoder : IPolylineEncoder { - private readonly PolylineEncoder _inner; - - /// - /// Initializes a new instance of the class. - /// - internal NetTopologyPolylineEncoder() { - PolylineFormatter formatter = - FormatterBuilder.Create() - // NetTopologySuite Point: Y = latitude, X = longitude - .AddValue("lat", static p => { ArgumentNullException.ThrowIfNull(p); return p.Y; }) - .AddValue("lon", static p => { ArgumentNullException.ThrowIfNull(p); return p.X; }) - .ForPolyline( - static m => m.IsEmpty ? string.Empty : new string(m.Span), - static s => s.AsMemory()) - .Build(); - - _inner = new PolylineEncoder(new PolylineOptions(formatter)); - } - - /// - public string Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) - => _inner.Encode(coordinates, cancellationToken); -} \ No newline at end of file diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/Program.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/Program.cs index ca9a8a6f..3f91db95 100644 --- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/Program.cs +++ b/samples/PolylineAlgorithm.NetTopologySuite.Sample/Program.cs @@ -4,10 +4,27 @@ // using NetTopologySuite.Geometries; -using PolylineAlgorithm.NetTopologySuite.Sample; +using PolylineAlgorithm; public class Program { public static void Main(string[] args) { + // Build a formatter for NetTopologySuite's Point type. + // NTS convention: Y = latitude, X = longitude. + PolylineFormatter formatter = + FormatterBuilder.Create() + .AddValue("lat", static p => p.Y) + .AddValue("lon", static p => p.X) + // The formatter automatically denormalizes scaled values, so v[0] = latitude, v[1] = longitude. + .WithCreate(static v => new Point(x: v[1], y: v[0])) + .ForPolyline( + static m => m.IsEmpty ? string.Empty : new string(m.Span), + static s => s.AsMemory()) + .Build(); + + PolylineOptions options = new(formatter); + PolylineEncoder encoder = new(options); + PolylineDecoder decoder = new(options); + // Sample route: Seattle → Bellevue → Redmond var points = new Point[] { @@ -16,9 +33,6 @@ public static void Main(string[] args) { new(x: -122.1215, y: 47.6740), // Redmond }; - var encoder = new NetTopologyPolylineEncoder(); - var decoder = new NetTopologyPolylineDecoder(); - // Encode string encoded = encoder.Encode(points); diff --git a/samples/PolylineAlgorithm.SensorData.Sample/Program.cs b/samples/PolylineAlgorithm.SensorData.Sample/Program.cs index e14977f9..05a0637a 100644 --- a/samples/PolylineAlgorithm.SensorData.Sample/Program.cs +++ b/samples/PolylineAlgorithm.SensorData.Sample/Program.cs @@ -3,10 +3,34 @@ // Licensed under the MIT License. See LICENSE file in the project root for full license information. // +using PolylineAlgorithm; using PolylineAlgorithm.SensorData.Sample; +using System; public static class Program { + // 2020-01-01 00:00:00 UTC in Unix seconds. Used as the delta-encoding baseline for timestamps + // so that the first absolute delta stays within the int32 safe range of the polyline algorithm. + private const long TimestampBaseEpoch = 1_577_836_800L; + public static void Main(string[] args) { + // Build a formatter for SensorReading: timestamp (Unix seconds, precision 0) + temperature. + // SetBaseline keeps the first timestamp delta small; the formatter adds it back when decoding. + PolylineFormatter formatter = + FormatterBuilder.Create() + .AddValue("timestamp", static r => (double)r.Timestamp.ToUnixTimeSeconds(), precision: 0) + .SetBaseline(TimestampBaseEpoch) + .AddValue("temperature", static r => r.Temperature, precision: 5) + // The formatter automatically denormalizes: v[0] = Unix seconds, v[1] = temperature. + .WithCreate(static v => new SensorReading( + DateTimeOffset.FromUnixTimeSeconds((long)v[0]), + v[1])) + .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .Build(); + + PolylineOptions options = new(formatter); + PolylineEncoder encoder = new(options); + PolylineDecoder decoder = new(options); + // Sample temperature readings from a sensor over six seconds var readings = new SensorReading[] { @@ -19,9 +43,6 @@ public static void Main(string[] args) { new(DateTimeOffset.UtcNow.AddSeconds(6), 22.3), }; - var encoder = new SensorDataEncoder(); - var decoder = new SensorDataDecoder(); - // Encode string encoded = encoder.Encode(readings); diff --git a/samples/PolylineAlgorithm.SensorData.Sample/SensorDataDecoder.cs b/samples/PolylineAlgorithm.SensorData.Sample/SensorDataDecoder.cs deleted file mode 100644 index d3fa0413..00000000 --- a/samples/PolylineAlgorithm.SensorData.Sample/SensorDataDecoder.cs +++ /dev/null @@ -1,107 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.SensorData.Sample; - -using PolylineAlgorithm.Abstraction; -using System.Collections.Generic; -using System.Threading; - -/// -/// Decodes a compact polyline string produced by back into a sequence -/// of values. -/// -/// -/// -/// This class demonstrates implementing for a custom -/// scalar type, following the same structural pattern as a custom polyline decoder. -/// -/// -/// Each encoded pair consists of a delta-compressed Unix timestamp (seconds since Unix epoch, precision 0) -/// followed by a delta-compressed temperature value (at ). -/// Both are recovered and used to reconstruct the original . -/// -/// -[System.Diagnostics.CodeAnalysis.SuppressMessage("Sonar", "S4456:Parameter validation in yielding methods should be wrapped", Justification = "Inlined by design to demonstrate a simple iterator without a wrapper method.")] -internal sealed class SensorDataDecoder : IPolylineDecoder { - /// - /// Initializes a new instance of the class with default encoding options. - /// - public SensorDataDecoder() - : this(new SensorEncodingOptions()) { } - - /// - /// Initializes a new instance of the class with the specified encoding options. - /// - /// - /// The to use for decoding operations. - /// The value must match the precision used during encoding. - /// - /// - /// Thrown when is . - /// - public SensorDataDecoder(SensorEncodingOptions options) { - ArgumentNullException.ThrowIfNull(options); - - Options = options; - } - - /// - /// Gets the encoding options used by this decoder. - /// - public SensorEncodingOptions Options { get; } - - /// - /// Decodes a polyline string back into a sequence of values. - /// - /// - /// The polyline-encoded string produced by . - /// - /// - /// A that can be used to cancel the decoding operation. - /// - /// - /// An of whose - /// and values - /// are recovered from the encoded string. - /// - /// - /// Thrown when is . - /// - /// - /// Thrown when is empty. - /// - /// - /// Thrown when requests cancellation. - /// - public IEnumerable Decode(string polyline, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(polyline); - - if (polyline.Length < 1) { - throw new ArgumentException("Encoded polyline must not be empty.", nameof(polyline)); - } - - ReadOnlyMemory memory = polyline.AsMemory(); - int position = 0; - // Mirror the encoder's base epoch so the first delta decodes back to the correct Unix seconds. - int accumulatedTimestamp = SensorDataEncoder.TimestampBaseEpochSeconds; - int accumulatedTemperature = 0; - - while (position < memory.Length) { - cancellationToken.ThrowIfCancellationRequested(); - - // Read Unix timestamp delta (precision 0) then temperature delta. - if (!PolylineEncoding.TryReadValue(ref accumulatedTimestamp, memory, ref position) - || !PolylineEncoding.TryReadValue(ref accumulatedTemperature, memory, ref position)) { - yield break; - } - - long unixSeconds = (long)PolylineEncoding.Denormalize(accumulatedTimestamp, precision: 0); - double temperature = PolylineEncoding.Denormalize(accumulatedTemperature, Options.Precision); - - yield return new SensorReading(DateTimeOffset.FromUnixTimeSeconds(unixSeconds), temperature); - } - } -} diff --git a/samples/PolylineAlgorithm.SensorData.Sample/SensorDataEncoder.cs b/samples/PolylineAlgorithm.SensorData.Sample/SensorDataEncoder.cs deleted file mode 100644 index 872bd318..00000000 --- a/samples/PolylineAlgorithm.SensorData.Sample/SensorDataEncoder.cs +++ /dev/null @@ -1,135 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.SensorData.Sample; - -using PolylineAlgorithm.Abstraction; -using System.Buffers; -using System.Threading; - -/// -/// Encodes a sequence of values into a compact polyline string -/// using the polyline delta-encoding algorithm applied to both the -/// and fields. -/// -/// -/// -/// This class demonstrates implementing for a custom -/// scalar type, following the same structural pattern as a custom polyline encoder. -/// -/// -/// Because sensor readings carry two numeric dimensions (timestamp and temperature), the generic encoder -/// for geographic coordinate pairs is not used. Instead, static -/// helpers are called directly to perform normalisation, delta computation, and character-level encoding. -/// -/// -/// Each reading is encoded as a pair of delta-compressed values: -/// the Unix timestamp in seconds (precision 0) followed by the temperature (at ). -/// -/// -internal sealed class SensorDataEncoder : IPolylineEncoder { - // 2020-01-01 00:00:00 UTC in Unix seconds. Used as the delta-encoding base for timestamps - // so that the first absolute delta stays within the int32 safe range of the polyline algorithm. - internal const int TimestampBaseEpochSeconds = 1_577_836_800; - - /// - /// Initializes a new instance of the class with default encoding options. - /// - public SensorDataEncoder() - : this(new SensorEncodingOptions()) { } - - /// - /// Initializes a new instance of the class with the specified encoding options. - /// - /// - /// The to use for encoding operations. - /// - /// - /// Thrown when is . - /// - public SensorDataEncoder(SensorEncodingOptions options) { - ArgumentNullException.ThrowIfNull(options); - - Options = options; - } - - /// - /// Gets the encoding options used by this encoder. - /// - public SensorEncodingOptions Options { get; } - - /// - /// Encodes a sequence of values into a polyline string. - /// - /// - /// The sensor readings to encode. Each reading contributes a delta-compressed Unix timestamp - /// (seconds since Unix epoch, precision 0) and a delta-compressed temperature value. - /// Must contain at least one element. - /// - /// - /// A that can be used to cancel the encoding operation. - /// - /// - /// A polyline-encoded string representing the delta-compressed timestamp and temperature series. - /// - /// - /// Thrown when is empty. - /// - /// - /// Thrown when requests cancellation. - /// - public string Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) { - if (coordinates.Length < 1) { - throw new ArgumentException("Sequence must contain at least one element.", nameof(coordinates)); - } - - // Maximum number of ASCII characters required to encode a single 32-bit delta value - // using the polyline algorithm (ceil(32 bits / 5 bits per chunk) + sign bit = 7). - const int MaxEncodedCharsPerValue = 7; - - // Each reading encodes two values: Unix timestamp (precision 0) + temperature. - // The polyline algorithm uses signed int32 internally, limiting safe absolute values to ~1.07B. - // Current Unix time in seconds (~1.74B) exceeds this. We therefore delta-encode relative to - // 2020-01-01 00:00:00 UTC (= 1 577 836 800 s), keeping the initial delta well within range. - int previousTimestampNormalized = TimestampBaseEpochSeconds; - int previousTemperatureNormalized = 0; - int position = 0; - int length = coordinates.Length * 2 * MaxEncodedCharsPerValue; - - char[]? temp = length <= Options.StackAllocLimit - ? null - : ArrayPool.Shared.Rent(length); - - Span buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length); - - try { - for (int i = 0; i < coordinates.Length; i++) { - cancellationToken.ThrowIfCancellationRequested(); - - // Encode Unix timestamp in whole seconds (precision 0). - int normalizedTimestamp = PolylineEncoding.Normalize((double)coordinates[i].Timestamp.ToUnixTimeSeconds(), precision: 0); - int timestampDelta = normalizedTimestamp - previousTimestampNormalized; - - // Encode temperature at the configured precision. - int normalizedTemperature = PolylineEncoding.Normalize(coordinates[i].Temperature, Options.Precision); - int temperatureDelta = normalizedTemperature - previousTemperatureNormalized; - - if (!PolylineEncoding.TryWriteValue(timestampDelta, buffer, ref position) - || !PolylineEncoding.TryWriteValue(temperatureDelta, buffer, ref position)) { - throw new InvalidOperationException("Encoding buffer is too small to hold the encoded value."); - } - - previousTimestampNormalized = normalizedTimestamp; - previousTemperatureNormalized = normalizedTemperature; - } - - return buffer[..position].ToString(); - } finally { - if (temp is not null) { - ArrayPool.Shared.Return(temp); - } - } - } -} diff --git a/samples/PolylineAlgorithm.SensorData.Sample/SensorEncodingOptions.cs b/samples/PolylineAlgorithm.SensorData.Sample/SensorEncodingOptions.cs deleted file mode 100644 index 5ad66387..00000000 --- a/samples/PolylineAlgorithm.SensorData.Sample/SensorEncodingOptions.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.SensorData.Sample; - -/// -/// Encoding options used by and . -/// -internal sealed class SensorEncodingOptions { - /// - /// Initializes a new instance of the class with default values. - /// - public SensorEncodingOptions() - : this(precision: 5, stackAllocLimit: 512) { } - - /// - /// Initializes a new instance of the class. - /// - /// Number of decimal places to use when encoding floating-point values. Defaults to 5. - /// - /// Maximum number of characters to allocate on the stack during encoding. Defaults to 512. - /// - public SensorEncodingOptions(uint precision, int stackAllocLimit) { - Precision = precision; - StackAllocLimit = stackAllocLimit; - } - - /// - /// Gets the number of decimal places used when encoding floating-point values. - /// - public uint Precision { get; } - - /// - /// Gets the maximum number of characters to allocate on the stack during encoding. - /// - public int StackAllocLimit { get; } -} diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs index 09c52504..68dc2bb3 100644 --- a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs +++ b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs @@ -61,12 +61,15 @@ public interface IPolylineFormatter { ReadOnlyMemory Read(TPolyline polyline); /// - /// Reconstructs a from the given scaled integer values. - /// Called once per decoded item in the decoding loop. + /// Reconstructs a from the given accumulated scaled integer values. + /// Called once per decoded item in the decoding loop. Implementations are responsible for + /// denormalizing the raw scaled integers (e.g. dividing by the precision factor and adding back + /// any baseline) before constructing the item. /// /// - /// The accumulated scaled integer values decoded from the polyline. Each element corresponds to - /// the same column position as in . + /// The raw accumulated scaled integer values decoded from the polyline. Each element corresponds to + /// the same column position as in . These are the direct output of the + /// delta-accumulation loop in the decoder before any denormalization is applied. /// /// A reconstructed from . TCoordinate CreateItem(ReadOnlySpan values); diff --git a/src/PolylineAlgorithm/FormatterBuilder.cs b/src/PolylineAlgorithm/FormatterBuilder.cs index 15c39737..ae27133f 100644 --- a/src/PolylineAlgorithm/FormatterBuilder.cs +++ b/src/PolylineAlgorithm/FormatterBuilder.cs @@ -110,12 +110,14 @@ public FormatterBuilder SetBaseline(long baseline) { /// /// Registers a factory delegate used to reconstruct a from - /// scaled values during decoding. + /// denormalized values during decoding. /// /// - /// A delegate that accepts the scaled integer values decoded from the polyline and returns a - /// . The span length always equals the number of columns added - /// via . + /// A delegate that accepts the denormalized values reconstructed from the + /// polyline and returns a . The formatter automatically divides + /// each accumulated scaled integer by its precision factor and adds back any baseline configured + /// via , so the span values match the original values supplied to the + /// encoder. The span length always equals the number of columns added via . /// /// The current builder instance for method chaining. /// diff --git a/src/PolylineAlgorithm/PolylineFormatter.cs b/src/PolylineAlgorithm/PolylineFormatter.cs index 134a984d..d9c1636c 100644 --- a/src/PolylineAlgorithm/PolylineFormatter.cs +++ b/src/PolylineAlgorithm/PolylineFormatter.cs @@ -89,6 +89,16 @@ public TCoordinate CreateItem(ReadOnlySpan values) { $"Call {nameof(FormatterBuilder)}.{nameof(FormatterBuilder.WithCreate)} before building."); } - return _create(values); + // Denormalize each accumulated scaled integer back to the original double: + // add back the baseline that was subtracted during encoding, then divide by the precision factor. + var rules = _rules; + int width = rules.Length; + double[] doubles = new double[width]; + for (var i = 0; i < width; i++) { + ref var rule = ref rules[i]; + doubles[i] = (values[i] + (rule.Baseline ?? 0L)) / (double)rule.Factor; + } + + return _create(doubles); } } diff --git a/src/PolylineAlgorithm/PolylineItemFactory.cs b/src/PolylineAlgorithm/PolylineItemFactory.cs index 56832992..e2e5e23a 100644 --- a/src/PolylineAlgorithm/PolylineItemFactory.cs +++ b/src/PolylineAlgorithm/PolylineItemFactory.cs @@ -8,13 +8,15 @@ namespace PolylineAlgorithm; using System; /// -/// Represents a factory method that reconstructs a item from an array of -/// scaled integer values decoded from a polyline. +/// Represents a factory method that reconstructs a item from denormalized +/// values decoded from a polyline. /// /// The coordinate or item type to create. /// -/// The scaled integer values accumulated from the polyline decoder. Each element corresponds to one -/// column as defined by the that built the associated formatter. +/// The denormalized values reconstructed by the polyline decoder. Each element corresponds to the +/// original value that was encoded, with the precision factor divided out and any +/// baseline added back. The span length equals the number of columns defined via +/// . /// /// A instance reconstructed from . -public delegate T PolylineItemFactory(ReadOnlySpan values); +public delegate T PolylineItemFactory(ReadOnlySpan values); diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index d01a6b46..15041b6e 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -25,7 +25,7 @@ PolylineAlgorithm.PolylineFormatter.Read(TPolyline polyl PolylineAlgorithm.PolylineFormatter.Width.get -> int PolylineAlgorithm.PolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline PolylineAlgorithm.PolylineItemFactory -virtual PolylineAlgorithm.PolylineItemFactory.Invoke(System.ReadOnlySpan values) -> T +virtual PolylineAlgorithm.PolylineItemFactory.Invoke(System.ReadOnlySpan values) -> T PolylineAlgorithm.PolylineOptions PolylineAlgorithm.PolylineOptions.Formatter.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! PolylineAlgorithm.PolylineOptions.LoggerFactory.get -> Microsoft.Extensions.Logging.ILoggerFactory! diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs index e8176817..e1a1d3f8 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs @@ -27,7 +27,7 @@ public sealed class AbstractPolylineDecoderTests { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", c => c.Lat) .AddValue("lon", c => c.Lon) - .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .WithCreate(static v => (v[0], v[1])) .ForPolyline(_write, _read) .Build(); diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs index 7f8d270a..a00d6417 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs @@ -200,7 +200,7 @@ public void Encode_RoundTrip_Produces_Original_Coordinates() { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", c => c.Lat) .AddValue("lon", c => c.Lon) - .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .WithCreate(static v => (v[0], v[1])) .ForPolyline(_write, _read) .Build(); diff --git a/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs index c4ec4879..3a847760 100644 --- a/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs +++ b/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs @@ -22,7 +22,7 @@ public sealed class PolylineDecoderExtensionsTests { FormatterBuilder<(double Latitude, double Longitude), string>.Create() .AddValue("lat", static c => c.Latitude) .AddValue("lon", static c => c.Longitude) - .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .WithCreate(static v => (v[0], v[1])) .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) .Build(); @@ -35,7 +35,7 @@ public sealed class PolylineDecoderExtensionsTests { FormatterBuilder<(double Latitude, double Longitude), ReadOnlyMemory>.Create() .AddValue("lat", static c => c.Latitude) .AddValue("lon", static c => c.Longitude) - .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .WithCreate(static v => (v[0], v[1])) .ForPolyline(static m => m, static m => m) .Build(); diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs index de5cdd06..96d5df52 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs @@ -98,7 +98,7 @@ public void WithCreate_Returns_Same_Builder_For_Method_Chaining() { .AddValue("Value", static v => v); // Act - FormatterBuilder result = builder.WithCreate(static v => v[0] / 1e5); + FormatterBuilder result = builder.WithCreate(static v => v[0]); // Assert Assert.AreSame(builder, result); @@ -162,14 +162,15 @@ public void CreateItem_Without_Factory_Throws_InvalidOperationException() { /// Tests that CreateItem with a factory correctly constructs the item. [TestMethod] public void CreateItem_With_Factory_Returns_Expected_Value() { - // Arrange — precision 5, factory divides by 1e5 + // Arrange — precision 5; the formatter automatically divides the accumulated scaled integer + // by the factor (1e5), so the factory receives the denormalized double directly. PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) - .WithCreate(static v => v[0] / 1e5) + .WithCreate(static v => v[0]) .ForPolyline(_write, _read) .Build(); - // Act — 3850000 / 100000.0 = 38.5 + // Act — accumulated value 3850000 → 3850000 / 100000.0 = 38.5 passed to factory double result = formatter.CreateItem(new long[] { 3850000L }.AsSpan()); // Assert @@ -184,7 +185,7 @@ public void CreateItem_With_Multi_Value_Factory_Returns_Expected_Tuple() { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("Lat", static t => t.Lat, precision: 5) .AddValue("Lon", static t => t.Lon, precision: 5) - .WithCreate(static v => (v[0] / 1e5, v[1] / 1e5)) + .WithCreate(static v => (v[0], v[1])) .ForPolyline(_write, _read) .Build(); From 2b4ca889bca25f42b5dd9daac62d6210a698aeed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:36:08 +0000 Subject: [PATCH 12/22] chore: remove api-reference/0.0 folder and clear PublicAPI.Unshipped.txt Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/a87601c1-462f-4061-b9ff-8948e56da5c6 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- ....Abstraction.AbstractPolylineDecoder-2.yml | 312 ----------- ....Abstraction.AbstractPolylineEncoder-2.yml | 306 ---------- ...gorithm.Abstraction.IPolylineDecoder-2.yml | 90 --- ...gorithm.Abstraction.IPolylineEncoder-2.yml | 142 ----- ...rithm.Abstraction.IPolylineFormatter-1.yml | 105 ---- ....Abstraction.IPolylineValueFormatter-1.yml | 153 ----- .../0.0/PolylineAlgorithm.Abstraction.yml | 50 -- ...m.Extensions.PolylineDecoderExtensions.yml | 195 ------- ...m.Extensions.PolylineEncoderExtensions.yml | 139 ----- .../0.0/PolylineAlgorithm.Extensions.yml | 19 - .../PolylineAlgorithm.FormatterBuilder-1.yml | 241 -------- ...lineAlgorithm.InvalidPolylineException.yml | 102 ---- .../PolylineAlgorithm.PolylineEncoding.yml | 529 ------------------ ...ylineAlgorithm.PolylineEncodingOptions.yml | 127 ----- ...gorithm.PolylineEncodingOptionsBuilder.yml | 141 ----- .../PolylineAlgorithm.PolylineFormatter.yml | 168 ------ ...olylineAlgorithm.PolylineItemFactory-1.yml | 50 -- .../PolylineAlgorithm.PolylineOptions-2.yml | 157 ------ ...lineAlgorithm.PolylineValueFormatter-1.yml | 226 -------- api-reference/0.0/PolylineAlgorithm.yml | 71 --- api-reference/0.0/toc.yml | 49 -- src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 57 -- 22 files changed, 3429 deletions(-) delete mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.Extensions.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.FormatterBuilder-1.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineFormatter.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineValueFormatter-1.yml delete mode 100644 api-reference/0.0/PolylineAlgorithm.yml delete mode 100644 api-reference/0.0/toc.yml diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml deleted file mode 100644 index 8b4d27f8..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml +++ /dev/null @@ -1,312 +0,0 @@ -### YamlMime:ApiPage -title: Class AbstractPolylineDecoder -body: -- api1: Class AbstractPolylineDecoder - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L31 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2 - commentId: T:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm.Abstraction - url: PolylineAlgorithm.Abstraction.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Provides a base implementation for decoding encoded polyline strings into sequences of geographic coordinates. -- code: 'public class AbstractPolylineDecoder : IPolylineDecoder' -- h4: Type Parameters -- parameters: - - name: TPolyline - description: The type that represents the encoded polyline input. - - name: TCoordinate - description: The type that represents a decoded geographic coordinate. -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: AbstractPolylineDecoder - url: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.html -- h4: Implements -- list: - - text: IPolylineDecoder - url: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.MemberwiseClone() - url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Remarks -- markdown: >- -

- - Formatter-based use (no subclassing required): - - Supply a via the - - - - constructor. The formatters handle all type-specific concerns; override nothing. - -

- -

- - Legacy override-based use: - - Derive from this class and override and - - to provide type-specific behaviour. These overrides take priority over any registered formatter. - -

-- h2: Constructors -- api3: AbstractPolylineDecoder() - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2__ctor - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L39 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor -- markdown: Initializes a new instance of the class with default encoding options. -- code: protected AbstractPolylineDecoder() -- api3: AbstractPolylineDecoder(PolylineEncodingOptions) - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2__ctor_PolylineAlgorithm_PolylineEncodingOptions_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L51 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions) -- markdown: Initializes a new instance of the class with the specified encoding options. -- code: protected AbstractPolylineDecoder(PolylineEncodingOptions options) -- h4: Parameters -- parameters: - - name: options - type: - - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html - description: The to use for encoding operations. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when options is null. -- api3: AbstractPolylineDecoder(PolylineOptions) - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2__ctor_PolylineAlgorithm_PolylineOptions__1__0__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L78 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`1,`0}) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`1,`0}) -- markdown: >- - Initializes a new instance of the class - - using the supplied . -- code: public AbstractPolylineDecoder(PolylineOptions options) -- h4: Parameters -- parameters: - - name: options - type: - - text: PolylineOptions - url: PolylineAlgorithm.PolylineOptions-2.html - - < - - TCoordinate - - ',' - - " " - - TPolyline - - '>' - description: >- - A that carries both the value formatter and - - the polyline formatter together with the underlying . -- h4: Remarks -- markdown: >- - Use this constructor when you want formatter-driven decoding without subclassing. - - The and hooks are not called; - - all type-specific logic is delegated to the formatters. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when options is null. -- h2: Properties -- api3: Options - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Options - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L94 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Options - commentId: P:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Options -- markdown: Gets the encoding options used by this polyline decoder. -- code: public PolylineEncodingOptions Options { get; } -- h4: Property Value -- parameters: - - type: - - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html -- h2: Methods -- api3: CreateCoordinate(double, double) - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_CreateCoordinate_System_Double_System_Double_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L286 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.CreateCoordinate(System.Double,System.Double) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.CreateCoordinate(System.Double,System.Double) -- markdown: Creates a TCoordinate instance from the specified latitude and longitude values. -- code: protected virtual TCoordinate CreateCoordinate(double latitude, double longitude) -- h4: Parameters -- parameters: - - name: latitude - type: - - text: double - url: https://learn.microsoft.com/dotnet/api/system.double - description: The latitude component of the coordinate, in degrees. - - name: longitude - type: - - text: double - url: https://learn.microsoft.com/dotnet/api/system.double - description: The longitude component of the coordinate, in degrees. -- h4: Returns -- parameters: - - type: - - TCoordinate - description: A TCoordinate instance representing the specified geographic coordinate. -- h4: Exceptions -- parameters: - - type: - - text: NotSupportedException - url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception - description: >- - Thrown by the default implementation when no value formatter is registered and the method - - has not been overridden in a derived class. -- api3: Decode(TPolyline, CancellationToken) - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_Decode__0_System_Threading_CancellationToken_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L121 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken) -- markdown: >- - Decodes an encoded TPolyline into a sequence of TCoordinate instances, - - with support for cancellation. -- code: >- - [SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains two path implementations.")] - - public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default) -- h4: Parameters -- parameters: - - name: polyline - type: - - TPolyline - description: The TPolyline instance containing the encoded polyline string to decode. - - name: cancellationToken - type: - - text: CancellationToken - url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken - description: A that can be used to cancel the decoding operation. - optional: true -- h4: Returns -- parameters: - - type: - - text: IEnumerable - url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1 - - < - - TCoordinate - - '>' - description: An of TCoordinate representing the decoded latitude and longitude pairs. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when polyline is null. - - type: - - text: ArgumentException - url: https://learn.microsoft.com/dotnet/api/system.argumentexception - description: Thrown when polyline is empty. - - type: - - text: InvalidPolylineException - url: PolylineAlgorithm.InvalidPolylineException.html - description: Thrown when the polyline format is invalid or malformed at a specific position. - - type: - - text: OperationCanceledException - url: https://learn.microsoft.com/dotnet/api/system.operationcanceledexception - description: Thrown when cancellationToken is canceled during decoding. -- api3: GetReadOnlyMemory(in TPolyline) - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_GetReadOnlyMemory__0__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L263 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.GetReadOnlyMemory(`0@) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.GetReadOnlyMemory(`0@) -- markdown: Extracts the underlying read-only memory region of characters from the specified polyline instance. -- code: protected virtual ReadOnlyMemory GetReadOnlyMemory(in TPolyline polyline) -- h4: Parameters -- parameters: - - name: polyline - type: - - TPolyline - description: The TPolyline instance from which to extract the character sequence. -- h4: Returns -- parameters: - - type: - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: A of representing the encoded polyline characters. -- h4: Exceptions -- parameters: - - type: - - text: NotSupportedException - url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception - description: >- - Thrown by the default implementation when no polyline formatter is registered and the method - - has not been overridden in a derived class. -- api3: ValidateFormat(ReadOnlyMemory, ILogger?) - id: PolylineAlgorithm_Abstraction_AbstractPolylineDecoder_2_ValidateFormat_System_ReadOnlyMemory_System_Char__Microsoft_Extensions_Logging_ILogger_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs#L239 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.ValidateFormat(System.ReadOnlyMemory{System.Char},Microsoft.Extensions.Logging.ILogger) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineDecoder`2.ValidateFormat(System.ReadOnlyMemory{System.Char},Microsoft.Extensions.Logging.ILogger) -- markdown: Validates the format of the polyline character sequence, ensuring all characters are within the allowed range. -- code: protected virtual void ValidateFormat(ReadOnlyMemory sequence, ILogger? logger) -- h4: Parameters -- parameters: - - name: sequence - type: - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: The read-only memory region of characters representing the polyline to validate. - - name: logger - type: - - text: ILogger - url: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger - - '?' - description: An optional used to log a warning when format validation fails. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentException - url: https://learn.microsoft.com/dotnet/api/system.argumentexception - description: Thrown when the polyline contains characters outside the valid encoding range or has an invalid block structure. -languageId: csharp -metadata: - description: Provides a base implementation for decoding encoded polyline strings into sequences of geographic coordinates. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml deleted file mode 100644 index ea9654c1..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml +++ /dev/null @@ -1,306 +0,0 @@ -### YamlMime:ApiPage -title: Class AbstractPolylineEncoder -body: -- api1: Class AbstractPolylineEncoder - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L37 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2 - commentId: T:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm.Abstraction - url: PolylineAlgorithm.Abstraction.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Provides a base implementation for encoding sequences of geographic coordinates into encoded polyline strings. -- code: 'public class AbstractPolylineEncoder : IPolylineEncoder' -- h4: Type Parameters -- parameters: - - name: TCoordinate - description: The type that represents a geographic coordinate to encode. - - name: TPolyline - description: The type that represents the encoded polyline output. -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: AbstractPolylineEncoder - url: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.html -- h4: Implements -- list: - - text: IPolylineEncoder - url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.MemberwiseClone() - url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h4: Extension Methods -- list: - - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, List) - url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1__System_Collections_Generic_List___0__ - - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, TCoordinate[]) - url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___ -- h2: Remarks -- markdown: >- -

- - Formatter-based use (no subclassing required): - - Supply a via the - - - - constructor. The formatter handles all type-specific concerns; override nothing. - -

- -

- - Legacy override-based use: - - Derive from this class and override , , - - and to provide type-specific behaviour. These overrides take - - priority over any registered formatter. - -

-- h2: Constructors -- api3: AbstractPolylineEncoder() - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2__ctor - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L45 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor -- markdown: Initializes a new instance of the class with default encoding options. -- code: protected AbstractPolylineEncoder() -- api3: AbstractPolylineEncoder(PolylineEncodingOptions) - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2__ctor_PolylineAlgorithm_PolylineEncodingOptions_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L55 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineEncodingOptions) -- markdown: Initializes a new instance of the class with the specified encoding options. -- code: protected AbstractPolylineEncoder(PolylineEncodingOptions options) -- h4: Parameters -- parameters: - - name: options - type: - - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html - description: The to use for encoding operations. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when options is null -- api3: AbstractPolylineEncoder(PolylineOptions) - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2__ctor_PolylineAlgorithm_PolylineOptions__0__1__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L82 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`0,`1}) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`0,`1}) -- markdown: >- - Initializes a new instance of the class - - using the supplied . -- code: public AbstractPolylineEncoder(PolylineOptions options) -- h4: Parameters -- parameters: - - name: options - type: - - text: PolylineOptions - url: PolylineAlgorithm.PolylineOptions-2.html - - < - - TCoordinate - - ',' - - " " - - TPolyline - - '>' - description: >- - A that carries both the value formatter and - - the polyline formatter together with the underlying . -- h4: Remarks -- markdown: >- - Use this constructor when you want formatter-driven encoding without subclassing. - - The , , and hooks - - are not called; all type-specific logic is delegated to the formatters. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when options is null. -- h2: Properties -- api3: Options - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Options - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L98 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Options - commentId: P:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Options -- markdown: Gets the encoding options used by this polyline encoder. -- code: public PolylineEncodingOptions Options { get; } -- h4: Property Value -- parameters: - - type: - - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html -- h2: Methods -- api3: CreatePolyline(ReadOnlyMemory) - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_CreatePolyline_System_ReadOnlyMemory_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L266 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.CreatePolyline(System.ReadOnlyMemory{System.Char}) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.CreatePolyline(System.ReadOnlyMemory{System.Char}) -- markdown: Creates a polyline instance from the provided read-only sequence of characters. -- code: protected virtual TPolyline CreatePolyline(ReadOnlyMemory polyline) -- h4: Parameters -- parameters: - - name: polyline - type: - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: A containing the encoded polyline characters. -- h4: Returns -- parameters: - - type: - - TPolyline - description: An instance of TPolyline representing the encoded polyline. -- h4: Exceptions -- parameters: - - type: - - text: NotSupportedException - url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception - description: >- - Thrown by the default implementation when no polyline formatter is registered and the method - - has not been overridden in a derived class. -- api3: Encode(ReadOnlySpan, CancellationToken) - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_Encode_System_ReadOnlySpan__0__System_Threading_CancellationToken_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L121 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) -- markdown: Encodes a collection of TCoordinate instances into an encoded TPolyline string. -- code: >- - [SuppressMessage("Design", "MA0051:Method is too long", Justification = "Method contains local methods. Actual method only 55 lines.")] - - public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) -- h4: Parameters -- parameters: - - name: coordinates - type: - - text: ReadOnlySpan - url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 - - < - - TCoordinate - - '>' - description: The collection of TCoordinate objects to encode. - - name: cancellationToken - type: - - text: CancellationToken - url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken - description: A that can be used to cancel the encoding operation. - optional: true -- h4: Returns -- parameters: - - type: - - TPolyline - description: An instance of TPolyline representing the encoded coordinates. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when coordinates is null. - - type: - - text: ArgumentException - url: https://learn.microsoft.com/dotnet/api/system.argumentexception - description: Thrown when coordinates is an empty enumeration. - - type: - - text: InvalidOperationException - url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception - description: Thrown when the internal encoding buffer cannot accommodate the encoded value. -- api3: GetLatitude(TCoordinate) - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_GetLatitude__0_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L302 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLatitude(`0) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLatitude(`0) -- markdown: Extracts the latitude value from the specified coordinate. -- code: protected virtual double GetLatitude(TCoordinate current) -- h4: Parameters -- parameters: - - name: current - type: - - TCoordinate - description: The coordinate from which to extract the latitude. -- h4: Returns -- parameters: - - type: - - text: double - url: https://learn.microsoft.com/dotnet/api/system.double - description: The latitude value as a . -- h4: Exceptions -- parameters: - - type: - - text: NotSupportedException - url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception - description: >- - Thrown by the default implementation when no value formatter is registered and the method - - has not been overridden in a derived class. -- api3: GetLongitude(TCoordinate) - id: PolylineAlgorithm_Abstraction_AbstractPolylineEncoder_2_GetLongitude__0_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs#L284 - metadata: - uid: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLongitude(`0) - commentId: M:PolylineAlgorithm.Abstraction.AbstractPolylineEncoder`2.GetLongitude(`0) -- markdown: Extracts the longitude value from the specified coordinate. -- code: protected virtual double GetLongitude(TCoordinate current) -- h4: Parameters -- parameters: - - name: current - type: - - TCoordinate - description: The coordinate from which to extract the longitude. -- h4: Returns -- parameters: - - type: - - text: double - url: https://learn.microsoft.com/dotnet/api/system.double - description: The longitude value as a . -- h4: Exceptions -- parameters: - - type: - - text: NotSupportedException - url: https://learn.microsoft.com/dotnet/api/system.notsupportedexception - description: >- - Thrown by the default implementation when no value formatter is registered and the method - - has not been overridden in a derived class. -languageId: csharp -metadata: - description: Provides a base implementation for encoding sequences of geographic coordinates into encoded polyline strings. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml deleted file mode 100644 index d4c991f8..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml +++ /dev/null @@ -1,90 +0,0 @@ -### YamlMime:ApiPage -title: Interface IPolylineDecoder -body: -- api1: Interface IPolylineDecoder - id: PolylineAlgorithm_Abstraction_IPolylineDecoder_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L22 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineDecoder`2 - commentId: T:PolylineAlgorithm.Abstraction.IPolylineDecoder`2 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm.Abstraction - url: PolylineAlgorithm.Abstraction.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Defines a contract for decoding an encoded polyline into a sequence of geographic coordinates. -- code: public interface IPolylineDecoder -- h4: Type Parameters -- parameters: - - name: TPolyline - description: >- - The type that represents the encoded polyline input. Common implementations use , - - but custom wrapper types are allowed to carry additional metadata. - - name: TValue - description: >- - The coordinate type returned by the decoder. Typical implementations return a struct or class that - - contains latitude and longitude (for example a LatLng type or a ValueTuple<double,double>). -- h2: Methods -- api3: Decode(TPolyline, CancellationToken) - id: PolylineAlgorithm_Abstraction_IPolylineDecoder_2_Decode__0_System_Threading_CancellationToken_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L48 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken) - commentId: M:PolylineAlgorithm.Abstraction.IPolylineDecoder`2.Decode(`0,System.Threading.CancellationToken) -- markdown: >- - Decodes the specified encoded polyline into an ordered sequence of geographic coordinates. - - The sequence preserves the original vertex order encoded by the polyline. -- code: IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default) -- h4: Parameters -- parameters: - - name: polyline - type: - - TPolyline - description: >- - The TPolyline instance containing the encoded polyline to decode. - - Implementations SHOULD validate the input and may throw - - or for invalid formats. - - name: cancellationToken - type: - - text: CancellationToken - url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken - description: >- - A to observe while decoding. If cancellation is requested, - - implementations SHOULD stop work and throw an . - optional: true -- h4: Returns -- parameters: - - type: - - text: IEnumerable - url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1 - - < - - TValue - - '>' - description: >- - An of TValue representing the decoded - - latitude/longitude pairs (or equivalent coordinates) in the same order they were encoded. -- h4: Remarks -- markdown: >- - Implementations commonly follow the Google Encoded Polyline Algorithm Format, but this interface - - does not mandate a specific encoding. Consumers should rely on a concrete decoder's documentation - - to understand the exact encoding supported. -- h4: Exceptions -- parameters: - - type: - - text: OperationCanceledException - url: https://learn.microsoft.com/dotnet/api/system.operationcanceledexception - description: Thrown when the provided cancellationToken requests cancellation. -languageId: csharp -metadata: - description: Defines a contract for decoding an encoded polyline into a sequence of geographic coordinates. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml deleted file mode 100644 index 171811e9..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml +++ /dev/null @@ -1,142 +0,0 @@ -### YamlMime:ApiPage -title: Interface IPolylineEncoder -body: -- api1: Interface IPolylineEncoder - id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L36 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2 - commentId: T:PolylineAlgorithm.Abstraction.IPolylineEncoder`2 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm.Abstraction - url: PolylineAlgorithm.Abstraction.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: >- - Contract for encoding a sequence of geographic coordinates into an encoded polyline representation. - - Implementations interpret the generic TValue type and produce an encoded - - representation of those coordinates as TPolyline. -- code: public interface IPolylineEncoder -- h4: Type Parameters -- parameters: - - name: TValue - description: >- - The concrete coordinate representation used by the encoder (for example a struct or class containing - - Latitude and Longitude values). Implementations must document the expected shape, - - units (typically decimal degrees), and any required fields for TValue. - - Common shapes: - - - A struct or class with two double properties named Latitude and Longitude. - - - A tuple-like type (for example ValueTuple<double,double>) where the encoder documents - which element represents latitude and longitude. - - name: TPolyline - description: >- - The encoded polyline representation returned by the encoder (for example string, - - ReadOnlyMemory<char>, or a custom wrapper type). Concrete implementations should document - - the chosen representation and any memory / ownership expectations. -- h4: Extension Methods -- list: - - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, List) - url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1__System_Collections_Generic_List___0__ - - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, TValue[]) - url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___ -- h2: Remarks -- markdown: >- - - This interface is intentionally minimal to allow different encoding strategies (Google encoded polyline, - precision/scale variants, or custom compressed formats) to be expressed behind a common contract. - - Implementations should document: - - Coordinate precision and rounding rules (for example 1e-5 for 5-decimal precision). - - Coordinate ordering and whether altitude or additional dimensions are supported. - - Thread-safety guarantees: whether instances are safe to reuse concurrently or must be instantiated per-call. - - Implementations are encouraged to be memory-efficient; the API accepts a - to avoid forced allocations when callers already have contiguous memory. -- h2: Methods -- api3: Encode(ReadOnlySpan, CancellationToken) - id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2_Encode_System_ReadOnlySpan__0__System_Threading_CancellationToken_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L76 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) - commentId: M:PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) -- markdown: >- - Encodes a sequence of geographic coordinates into an encoded polyline representation. - - The order of coordinates in coordinates is preserved in the encoded result. -- code: TPolyline Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) -- h4: Parameters -- parameters: - - name: coordinates - type: - - text: ReadOnlySpan - url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 - - < - - TValue - - '>' - description: >- - The collection of TValue instances to encode into a polyline. - - The span may be empty; implementations should return an appropriate empty encoded representation - - (for example an empty string or an empty memory slice) rather than null. - - name: cancellationToken - type: - - text: CancellationToken - url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken - description: >- - A that can be used to cancel the encoding operation. - - Implementations should observe this token and throw - - when cancellation is requested. For fast, in-memory encoders cancellation may be best-effort. - optional: true -- h4: Returns -- parameters: - - type: - - TPolyline - description: >- - A TPolyline containing the encoded polyline that represents the input coordinates. - - The exact format and any delimiting/terminating characters are implementation-specific and must be - - documented by concrete encoder types. -- h4: Examples -- markdown: >- -
// Example pseudocode for typical usage with a string-based encoder:
-
-    var coords = new[] {
-        new Coordinate { Latitude = 47.6219, Longitude = -122.3503 },
-        new Coordinate { Latitude = 47.6220, Longitude = -122.3504 }
-    };
-
-    IPolylineEncoder<Coordinate,string> encoder = new GoogleEncodedPolylineEncoder();
-
-    string encoded = encoder.Encode(coords, CancellationToken.None);
-- h4: Remarks -- markdown: >- - - Implementations should validate input as appropriate and document any preconditions (for example - if coordinates must be within [-90,90] latitude and [-180,180] longitude). - - For large input sequences, implementations may provide streaming or incremental encoders; those - variants can still implement this interface by materializing the final encoded result. -- h4: Exceptions -- parameters: - - type: - - text: OperationCanceledException - url: https://learn.microsoft.com/dotnet/api/system.operationcanceledexception - description: Thrown if the operation is canceled via cancellationToken. -languageId: csharp -metadata: - description: >- - Contract for encoding a sequence of geographic coordinates into an encoded polyline representation. - - Implementations interpret the generic TValue type and produce an encoded - - representation of those coordinates as TPolyline. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml deleted file mode 100644 index c1d7296b..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml +++ /dev/null @@ -1,105 +0,0 @@ -### YamlMime:ApiPage -title: Interface IPolylineFormatter -body: -- api1: Interface IPolylineFormatter - id: PolylineAlgorithm_Abstraction_IPolylineFormatter_1 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L28 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`1 - commentId: T:PolylineAlgorithm.Abstraction.IPolylineFormatter`1 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm.Abstraction - url: PolylineAlgorithm.Abstraction.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: >- - Defines how to produce a TPolyline from an encoded character buffer (output/write - - direction), and how to extract that buffer back from a TPolyline (input/read - - direction). -- code: public interface IPolylineFormatter -- h4: Type Parameters -- parameters: - - name: TPolyline - description: >- - The polyline surface type — for example or - of . -- h2: Remarks -- markdown: >- -

- - This interface is the polyline-surface counterpart to . - - The engine exclusively works with of internally. - - The formatter is the only code that touches TPolyline. - -

- -

- - Use , , or - - to obtain a ready-made implementation. - -

-- h2: Methods -- api3: Read(TPolyline) - id: PolylineAlgorithm_Abstraction_IPolylineFormatter_1_Read__0_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L41 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`1.Read(`0) - commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`1.Read(`0) -- markdown: Extracts the character buffer from a TPolyline for the decoder to read. -- code: ReadOnlyMemory Read(TPolyline polyline) -- h4: Parameters -- parameters: - - name: polyline - type: - - TPolyline - description: The polyline to read from. -- h4: Returns -- parameters: - - type: - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: A of representing the encoded characters. -- api3: Write(ReadOnlyMemory) - id: PolylineAlgorithm_Abstraction_IPolylineFormatter_1_Write_System_ReadOnlyMemory_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L34 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`1.Write(System.ReadOnlyMemory{System.Char}) - commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`1.Write(System.ReadOnlyMemory{System.Char}) -- markdown: Creates a TPolyline from the encoded character buffer produced by the encoder. -- code: TPolyline Write(ReadOnlyMemory encoded) -- h4: Parameters -- parameters: - - name: encoded - type: - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: The encoded polyline as a read-only span of characters. -- h4: Returns -- parameters: - - type: - - TPolyline - description: A TPolyline wrapping or derived from encoded. -languageId: csharp -metadata: - description: >- - Defines how to produce a TPolyline from an encoded character buffer (output/write - - direction), and how to extract that buffer back from a TPolyline (input/read - - direction). diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml deleted file mode 100644 index 0f5297f3..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml +++ /dev/null @@ -1,153 +0,0 @@ -### YamlMime:ApiPage -title: Interface IPolylineValueFormatter -body: -- api1: Interface IPolylineValueFormatter - id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L26 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1 - commentId: T:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm.Abstraction - url: PolylineAlgorithm.Abstraction.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: >- - Defines how to extract scaled numeric values from a TValue during encoding, and - - how to reconstruct a TValue from those values during decoding. -- code: public interface IPolylineValueFormatter -- h4: Type Parameters -- parameters: - - name: TValue - description: The coordinate or item type that the formatter understands. -- h2: Remarks -- markdown: >- -

- - This interface is the coordinate-side counterpart to . - - Together they allow the engine base classes to be used directly — without subclassing — by supplying - - both formatters via . - -

- -

- - Use to build a that - - already implements this interface. - -

-- h2: Properties -- api3: Width - id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1_Width - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L54 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.Width - commentId: P:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.Width -- markdown: >- - Gets the number of values (columns) per encoded item. - - This is the required length of the buffer passed to and - - the length of the span received in . -- code: int Width { get; } -- h4: Property Value -- parameters: - - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 -- h2: Methods -- api3: CreateItem(ReadOnlySpan) - id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1_CreateItem_System_ReadOnlySpan_System_Int64__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L47 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.CreateItem(System.ReadOnlySpan{System.Int64}) - commentId: M:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.CreateItem(System.ReadOnlySpan{System.Int64}) -- markdown: >- - Reconstructs a TValue from the given scaled integer values. - - Called once per decoded item in the decoding loop. -- code: TValue CreateItem(ReadOnlySpan values) -- h4: Parameters -- parameters: - - name: values - type: - - text: ReadOnlySpan - url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 - - < - - text: long - url: https://learn.microsoft.com/dotnet/api/system.int64 - - '>' - description: >- - The accumulated scaled integer values decoded from the polyline. Each element corresponds to - - the same column position as in . -- h4: Returns -- parameters: - - type: - - TValue - description: A TValue reconstructed from values. -- api3: GetBaseline(int) - id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1_GetBaseline_System_Int32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L62 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.GetBaseline(System.Int32) - commentId: M:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.GetBaseline(System.Int32) -- markdown: >- - Returns the baseline (epoch) for the column at index, or 0 if none is configured. - - The encoder subtracts this value from the first item's scaled column value to keep the initial delta small. -- code: long GetBaseline(int index) -- h4: Parameters -- parameters: - - name: index - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: The zero-based column index. Must be in the range [0, ). -- h4: Returns -- parameters: - - type: - - text: long - url: https://learn.microsoft.com/dotnet/api/system.int64 - description: The baseline value, or 0 when no baseline has been defined for the column. -- api3: GetValues(TValue, Span) - id: PolylineAlgorithm_Abstraction_IPolylineValueFormatter_1_GetValues__0_System_Span_System_Int64__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineValueFormatter.cs#L36 - metadata: - uid: PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.GetValues(`0,System.Span{System.Int64}) - commentId: M:PolylineAlgorithm.Abstraction.IPolylineValueFormatter`1.GetValues(`0,System.Span{System.Int64}) -- markdown: >- - Extracts and scales all column values from item into the values span. - - Called once per item in the encoding loop. -- code: void GetValues(TValue item, Span values) -- h4: Parameters -- parameters: - - name: item - type: - - TValue - description: The source item from which column values are extracted. - - name: values - type: - - text: Span - url: https://learn.microsoft.com/dotnet/api/system.span-1 - - < - - text: long - url: https://learn.microsoft.com/dotnet/api/system.int64 - - '>' - description: >- - Output buffer that receives the scaled integer values. Its length must equal the number of columns - - defined by this formatter. -languageId: csharp -metadata: - description: >- - Defines how to extract scaled numeric values from a TValue during encoding, and - - how to reconstruct a TValue from those values during decoding. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.yml deleted file mode 100644 index ff64bf1c..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Abstraction.yml +++ /dev/null @@ -1,50 +0,0 @@ -### YamlMime:ApiPage -title: Namespace PolylineAlgorithm.Abstraction -body: -- api1: Namespace PolylineAlgorithm.Abstraction - id: PolylineAlgorithm_Abstraction - metadata: - uid: PolylineAlgorithm.Abstraction - commentId: N:PolylineAlgorithm.Abstraction -- h3: Classes -- parameters: - - type: - text: AbstractPolylineDecoder - url: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.html - description: Provides a base implementation for decoding encoded polyline strings into sequences of geographic coordinates. - - type: - text: AbstractPolylineEncoder - url: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.html - description: Provides a base implementation for encoding sequences of geographic coordinates into encoded polyline strings. -- h3: Interfaces -- parameters: - - type: - text: IPolylineDecoder - url: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.html - description: Defines a contract for decoding an encoded polyline into a sequence of geographic coordinates. - - type: - text: IPolylineEncoder - url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html - description: >- - Contract for encoding a sequence of geographic coordinates into an encoded polyline representation. - - Implementations interpret the generic TValue type and produce an encoded - - representation of those coordinates as TPolyline. - - type: - text: IPolylineFormatter - url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html - description: >- - Defines how to produce a TPolyline from an encoded character buffer (output/write - - direction), and how to extract that buffer back from a TPolyline (input/read - - direction). - - type: - text: IPolylineValueFormatter - url: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.html - description: >- - Defines how to extract scaled numeric values from a TValue during encoding, and - - how to reconstruct a TValue from those values during decoding. -languageId: csharp diff --git a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml b/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml deleted file mode 100644 index 716776cf..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml +++ /dev/null @@ -1,195 +0,0 @@ -### YamlMime:ApiPage -title: Class PolylineDecoderExtensions -body: -- api1: Class PolylineDecoderExtensions - id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L16 - metadata: - uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions - commentId: T:PolylineAlgorithm.Extensions.PolylineDecoderExtensions -- facts: - - name: Namespace - value: - text: PolylineAlgorithm.Extensions - url: PolylineAlgorithm.Extensions.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Provides extension methods for the interface to facilitate decoding encoded polylines. -- code: public static class PolylineDecoderExtensions -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: PolylineDecoderExtensions - url: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.MemberwiseClone() - url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Methods -- api3: Decode(IPolylineDecoder, char[]) - id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode__1_PolylineAlgorithm_Abstraction_IPolylineDecoder_System_String___0__System_Char___ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L33 - metadata: - uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.String,``0},System.Char[]) - commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.String,``0},System.Char[]) -- markdown: Decodes an encoded polyline represented as a character array into a sequence of geographic coordinates. -- code: public static IEnumerable Decode(this IPolylineDecoder decoder, char[] polyline) -- h4: Parameters -- parameters: - - name: decoder - type: - - text: IPolylineDecoder - url: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.html - - < - - text: string - url: https://learn.microsoft.com/dotnet/api/system.string - - ',' - - " " - - TValue - - '>' - description: The instance used to perform the decoding operation. - - name: polyline - type: - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '[' - - ']' - description: The encoded polyline as a character array to decode. The array is converted to a string internally. -- h4: Returns -- parameters: - - type: - - text: IEnumerable - url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1 - - < - - TValue - - '>' - description: An of TValue containing the decoded coordinate pairs. -- h4: Type Parameters -- parameters: - - name: TValue - description: The coordinate type returned by the decoder. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when decoder or polyline is null. -- api3: Decode(IPolylineDecoder, ReadOnlyMemory) - id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode__1_PolylineAlgorithm_Abstraction_IPolylineDecoder_System_String___0__System_ReadOnlyMemory_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L61 - metadata: - uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.String,``0},System.ReadOnlyMemory{System.Char}) - commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.String,``0},System.ReadOnlyMemory{System.Char}) -- markdown: Decodes an encoded polyline represented as a read-only memory of characters into a sequence of geographic coordinates. -- code: public static IEnumerable Decode(this IPolylineDecoder decoder, ReadOnlyMemory polyline) -- h4: Parameters -- parameters: - - name: decoder - type: - - text: IPolylineDecoder - url: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.html - - < - - text: string - url: https://learn.microsoft.com/dotnet/api/system.string - - ',' - - " " - - TValue - - '>' - description: The instance used to perform the decoding operation. - - name: polyline - type: - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: The encoded polyline as a read-only memory of characters to decode. The memory is converted to a string internally. -- h4: Returns -- parameters: - - type: - - text: IEnumerable - url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1 - - < - - TValue - - '>' - description: An of TValue containing the decoded coordinate pairs. -- h4: Type Parameters -- parameters: - - name: TValue - description: The coordinate type returned by the decoder. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when decoder is null. -- api3: Decode(IPolylineDecoder, TValue>, string) - id: PolylineAlgorithm_Extensions_PolylineDecoderExtensions_Decode__1_PolylineAlgorithm_Abstraction_IPolylineDecoder_System_ReadOnlyMemory_System_Char____0__System_String_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs#L86 - metadata: - uid: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.ReadOnlyMemory{System.Char},``0},System.String) - commentId: M:PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode``1(PolylineAlgorithm.Abstraction.IPolylineDecoder{System.ReadOnlyMemory{System.Char},``0},System.String) -- markdown: >- - Decodes an encoded polyline string into a sequence of geographic coordinates, - - using a decoder that accepts of . -- code: public static IEnumerable Decode(this IPolylineDecoder, TValue> decoder, string polyline) -- h4: Parameters -- parameters: - - name: decoder - type: - - text: IPolylineDecoder - url: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.html - - < - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - - ',' - - " " - - TValue - - '>' - description: The instance used to perform the decoding operation. - - name: polyline - type: - - text: string - url: https://learn.microsoft.com/dotnet/api/system.string - description: The encoded polyline string to decode. The string is converted to internally. -- h4: Returns -- parameters: - - type: - - text: IEnumerable - url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1 - - < - - TValue - - '>' - description: An of TValue containing the decoded coordinate pairs. -- h4: Type Parameters -- parameters: - - name: TValue - description: The coordinate type returned by the decoder. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when decoder or polyline is null. -languageId: csharp -metadata: - description: Provides extension methods for the interface to facilitate decoding encoded polylines. diff --git a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml b/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml deleted file mode 100644 index 36fd5ef9..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml +++ /dev/null @@ -1,139 +0,0 @@ -### YamlMime:ApiPage -title: Class PolylineEncoderExtensions -body: -- api1: Class PolylineEncoderExtensions - id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L19 - metadata: - uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions - commentId: T:PolylineAlgorithm.Extensions.PolylineEncoderExtensions -- facts: - - name: Namespace - value: - text: PolylineAlgorithm.Extensions - url: PolylineAlgorithm.Extensions.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines. -- code: public static class PolylineEncoderExtensions -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: PolylineEncoderExtensions - url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.MemberwiseClone() - url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Methods -- api3: Encode(IPolylineEncoder, List) - id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1__System_Collections_Generic_List___0__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L37 - metadata: - uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},System.Collections.Generic.List{``0}) - commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},System.Collections.Generic.List{``0}) -- markdown: Encodes a of TCoordinate instances into an encoded polyline. -- code: >- - [SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "We need a list as we do need to marshal it as span.")] - - [SuppressMessage("Design", "MA0016:Prefer using collection abstraction instead of implementation", Justification = "We need a list as we do need to marshal it as span.")] - - public static TPolyline Encode(this IPolylineEncoder encoder, List coordinates) -- h4: Parameters -- parameters: - - name: encoder - type: - - text: IPolylineEncoder - url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html - - < - - TCoordinate - - ',' - - " " - - TPolyline - - '>' - description: The instance used to perform the encoding operation. - - name: coordinates - type: - - text: List - url: https://learn.microsoft.com/dotnet/api/system.collections.generic.list-1 - - < - - TCoordinate - - '>' - description: The list of TCoordinate objects to encode. -- h4: Returns -- parameters: - - type: - - TPolyline - description: A TPolyline instance representing the encoded polyline for the provided coordinates. -- h4: Type Parameters -- parameters: - - name: TCoordinate - description: The type that represents a geographic coordinate to encode. - - name: TPolyline - description: The type that represents the encoded polyline output. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when encoder or coordinates is null. -- api3: Encode(IPolylineEncoder, TCoordinate[]) - id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L73 - metadata: - uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},``0[]) - commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},``0[]) -- markdown: Encodes an array of TCoordinate instances into an encoded polyline. -- code: public static TPolyline Encode(this IPolylineEncoder encoder, TCoordinate[] coordinates) -- h4: Parameters -- parameters: - - name: encoder - type: - - text: IPolylineEncoder - url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html - - < - - TCoordinate - - ',' - - " " - - TPolyline - - '>' - description: The instance used to perform the encoding operation. - - name: coordinates - type: - - TCoordinate - - '[' - - ']' - description: The array of TCoordinate objects to encode. -- h4: Returns -- parameters: - - type: - - TPolyline - description: A TPolyline instance representing the encoded polyline for the provided coordinates. -- h4: Type Parameters -- parameters: - - name: TCoordinate - description: The type that represents a geographic coordinate to encode. - - name: TPolyline - description: The type that represents the encoded polyline output. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when encoder or coordinates is null. -languageId: csharp -metadata: - description: Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines. diff --git a/api-reference/0.0/PolylineAlgorithm.Extensions.yml b/api-reference/0.0/PolylineAlgorithm.Extensions.yml deleted file mode 100644 index c39da0ca..00000000 --- a/api-reference/0.0/PolylineAlgorithm.Extensions.yml +++ /dev/null @@ -1,19 +0,0 @@ -### YamlMime:ApiPage -title: Namespace PolylineAlgorithm.Extensions -body: -- api1: Namespace PolylineAlgorithm.Extensions - id: PolylineAlgorithm_Extensions - metadata: - uid: PolylineAlgorithm.Extensions - commentId: N:PolylineAlgorithm.Extensions -- h3: Classes -- parameters: - - type: - text: PolylineDecoderExtensions - url: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.html - description: Provides extension methods for the interface to facilitate decoding encoded polylines. - - type: - text: PolylineEncoderExtensions - url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html - description: Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines. -languageId: csharp diff --git a/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-1.yml b/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-1.yml deleted file mode 100644 index 4fd394df..00000000 --- a/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-1.yml +++ /dev/null @@ -1,241 +0,0 @@ -### YamlMime:ApiPage -title: Class FormatterBuilder -body: -- api1: Class FormatterBuilder - id: PolylineAlgorithm_FormatterBuilder_1 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L28 - metadata: - uid: PolylineAlgorithm.FormatterBuilder`1 - commentId: T:PolylineAlgorithm.FormatterBuilder`1 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Provides a fluent builder for constructing a . -- code: public sealed class FormatterBuilder -- h4: Type Parameters -- parameters: - - name: T - description: The source object type from which column values are extracted. -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: FormatterBuilder - url: PolylineAlgorithm.FormatterBuilder-1.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Remarks -- markdown: >- -

- - Use to obtain an instance, call once per column, - - optionally chain to specify an epoch for the most-recently added column, - - optionally chain to register a factory for the decoding direction, - - then call to produce the immutable . - -

- -

- - The builder is the only way to create a — its - - constructor is internal. - -

-- h2: Methods -- api3: AddValue(string, Func, uint) - id: PolylineAlgorithm_FormatterBuilder_1_AddValue_System_String_System_Func__0_System_Double__System_UInt32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L62 - metadata: - uid: PolylineAlgorithm.FormatterBuilder`1.AddValue(System.String,System.Func{`0,System.Double},System.UInt32) - commentId: M:PolylineAlgorithm.FormatterBuilder`1.AddValue(System.String,System.Func{`0,System.Double},System.UInt32) -- markdown: Adds a column with the specified value selector and precision. -- code: public FormatterBuilder AddValue(string name, Func selector, uint precision = 5) -- h4: Parameters -- parameters: - - name: name - type: - - text: string - url: https://learn.microsoft.com/dotnet/api/system.string - description: A unique, non-null, non-empty name that identifies the column. Used for diagnostics only. - - name: selector - type: - - text: Func - url: https://learn.microsoft.com/dotnet/api/system.func-2 - - < - - T - - ',' - - " " - - text: double - url: https://learn.microsoft.com/dotnet/api/system.double - - '>' - description: >- - A delegate that extracts the column's raw value from an item of type - - T. - - name: precision - type: - - text: uint - url: https://learn.microsoft.com/dotnet/api/system.uint32 - description: >- - The number of decimal places to preserve. Each extracted value is multiplied by - - 10^precision before encoding. Defaults to 5. - optional: true -- h4: Returns -- parameters: - - type: - - text: FormatterBuilder - url: PolylineAlgorithm.FormatterBuilder-1.html - - < - - T - - '>' - description: The current instance for method chaining. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when name or selector is null. - - type: - - text: ArgumentException - url: https://learn.microsoft.com/dotnet/api/system.argumentexception - description: Thrown when name is empty, or a rule with the same name already exists. -- api3: Build() - id: PolylineAlgorithm_FormatterBuilder_1_Build - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L137 - metadata: - uid: PolylineAlgorithm.FormatterBuilder`1.Build - commentId: M:PolylineAlgorithm.FormatterBuilder`1.Build -- markdown: Bakes all added rules into a sealed, immutable . -- code: public PolylineValueFormatter Build() -- h4: Returns -- parameters: - - type: - - text: PolylineValueFormatter - url: PolylineAlgorithm.PolylineValueFormatter-1.html - - < - - T - - '>' - description: An immutable whose rules can no longer be changed. -- h4: Exceptions -- parameters: - - type: - - text: InvalidOperationException - url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception - description: Thrown when no rules have been added. -- api3: Create() - id: PolylineAlgorithm_FormatterBuilder_1_Create - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L39 - metadata: - uid: PolylineAlgorithm.FormatterBuilder`1.Create - commentId: M:PolylineAlgorithm.FormatterBuilder`1.Create -- markdown: Creates a new instance. -- code: public static FormatterBuilder Create() -- h4: Returns -- parameters: - - type: - - text: FormatterBuilder - url: PolylineAlgorithm.FormatterBuilder-1.html - - < - - T - - '>' - description: A fresh with no rules. -- api3: SetBaseline(long) - id: PolylineAlgorithm_FormatterBuilder_1_SetBaseline_System_Int64_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L94 - metadata: - uid: PolylineAlgorithm.FormatterBuilder`1.SetBaseline(System.Int64) - commentId: M:PolylineAlgorithm.FormatterBuilder`1.SetBaseline(System.Int64) -- markdown: >- - Sets a baseline (epoch) on the most-recently added column. - - During encoding the baseline is subtracted from the first item's scaled column value, - - keeping the initial delta small when the absolute first value is large. -- code: public FormatterBuilder SetBaseline(long baseline) -- h4: Parameters -- parameters: - - name: baseline - type: - - text: long - url: https://learn.microsoft.com/dotnet/api/system.int64 - description: The baseline value to apply to the first item's column value. -- h4: Returns -- parameters: - - type: - - text: FormatterBuilder - url: PolylineAlgorithm.FormatterBuilder-1.html - - < - - T - - '>' - description: The current instance for method chaining. -- h4: Exceptions -- parameters: - - type: - - text: InvalidOperationException - url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception - description: Thrown when no rules have been added yet. Call before . -- api3: WithCreate(PolylineItemFactory) - id: PolylineAlgorithm_FormatterBuilder_1_WithCreate_PolylineAlgorithm_PolylineItemFactory__0__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L118 - metadata: - uid: PolylineAlgorithm.FormatterBuilder`1.WithCreate(PolylineAlgorithm.PolylineItemFactory{`0}) - commentId: M:PolylineAlgorithm.FormatterBuilder`1.WithCreate(PolylineAlgorithm.PolylineItemFactory{`0}) -- markdown: >- - Registers a factory delegate used to reconstruct a T from scaled values - - during decoding. This enables the decoding direction of . -- code: public FormatterBuilder WithCreate(PolylineItemFactory create) -- h4: Parameters -- parameters: - - name: create - type: - - text: PolylineItemFactory - url: PolylineAlgorithm.PolylineItemFactory-1.html - - < - - T - - '>' - description: >- - A delegate that accepts the scaled integer values decoded from the polyline and returns a - - T. The span length always equals the number of columns added via - - . -- h4: Returns -- parameters: - - type: - - text: FormatterBuilder - url: PolylineAlgorithm.FormatterBuilder-1.html - - < - - T - - '>' - description: The current instance for method chaining. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when create is null. -languageId: csharp -metadata: - description: Provides a fluent builder for constructing a . diff --git a/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml b/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml deleted file mode 100644 index 7bc75368..00000000 --- a/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml +++ /dev/null @@ -1,102 +0,0 @@ -### YamlMime:ApiPage -title: Class InvalidPolylineException -body: -- api1: Class InvalidPolylineException - id: PolylineAlgorithm_InvalidPolylineException - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L17 - metadata: - uid: PolylineAlgorithm.InvalidPolylineException - commentId: T:PolylineAlgorithm.InvalidPolylineException -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Exception thrown when a polyline is determined to be malformed or invalid during processing. -- code: 'public sealed class InvalidPolylineException : Exception, ISerializable' -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: Exception - url: https://learn.microsoft.com/dotnet/api/system.exception - - text: InvalidPolylineException - url: PolylineAlgorithm.InvalidPolylineException.html -- h4: Implements -- list: - - text: ISerializable - url: https://learn.microsoft.com/dotnet/api/system.runtime.serialization.iserializable -- h4: Inherited Members -- list: - - text: Exception.GetBaseException() - url: https://learn.microsoft.com/dotnet/api/system.exception.getbaseexception - - text: Exception.GetObjectData(SerializationInfo, StreamingContext) - url: https://learn.microsoft.com/dotnet/api/system.exception.getobjectdata - - text: Exception.GetType() - url: https://learn.microsoft.com/dotnet/api/system.exception.gettype - - text: Exception.ToString() - url: https://learn.microsoft.com/dotnet/api/system.exception.tostring - - text: Exception.Data - url: https://learn.microsoft.com/dotnet/api/system.exception.data - - text: Exception.HelpLink - url: https://learn.microsoft.com/dotnet/api/system.exception.helplink - - text: Exception.HResult - url: https://learn.microsoft.com/dotnet/api/system.exception.hresult - - text: Exception.InnerException - url: https://learn.microsoft.com/dotnet/api/system.exception.innerexception - - text: Exception.Message - url: https://learn.microsoft.com/dotnet/api/system.exception.message - - text: Exception.Source - url: https://learn.microsoft.com/dotnet/api/system.exception.source - - text: Exception.StackTrace - url: https://learn.microsoft.com/dotnet/api/system.exception.stacktrace - - text: Exception.TargetSite - url: https://learn.microsoft.com/dotnet/api/system.exception.targetsite - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Remarks -- markdown: This exception is used internally to indicate that a polyline string does not conform to the expected format or contains errors. -- h2: Constructors -- api3: InvalidPolylineException() - id: PolylineAlgorithm_InvalidPolylineException__ctor - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L22 - metadata: - uid: PolylineAlgorithm.InvalidPolylineException.#ctor - commentId: M:PolylineAlgorithm.InvalidPolylineException.#ctor -- markdown: Initializes a new instance of the class. -- code: public InvalidPolylineException() -- api3: InvalidPolylineException(string, Exception) - id: PolylineAlgorithm_InvalidPolylineException__ctor_System_String_System_Exception_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L43 - metadata: - uid: PolylineAlgorithm.InvalidPolylineException.#ctor(System.String,System.Exception) - commentId: M:PolylineAlgorithm.InvalidPolylineException.#ctor(System.String,System.Exception) -- markdown: Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. -- code: public InvalidPolylineException(string message, Exception innerException) -- h4: Parameters -- parameters: - - name: message - type: - - text: string - url: https://learn.microsoft.com/dotnet/api/system.string - description: The error message that explains the reason for the exception. - - name: innerException - type: - - text: Exception - url: https://learn.microsoft.com/dotnet/api/system.exception - description: The exception that is the cause of the current exception, or a null reference if no inner exception is specified. -languageId: csharp -metadata: - description: Exception thrown when a polyline is determined to be malformed or invalid during processing. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml deleted file mode 100644 index 7eb6fc73..00000000 --- a/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml +++ /dev/null @@ -1,529 +0,0 @@ -### YamlMime:ApiPage -title: Class PolylineEncoding -body: -- api1: Class PolylineEncoding - id: PolylineAlgorithm_PolylineEncoding - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L23 - metadata: - uid: PolylineAlgorithm.PolylineEncoding - commentId: T:PolylineAlgorithm.PolylineEncoding -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: >- - Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and de-normalizing - - geographic coordinate values. -- code: public static class PolylineEncoding -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: PolylineEncoding - url: PolylineAlgorithm.PolylineEncoding.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.MemberwiseClone() - url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Remarks -- markdown: >- - The class includes functionality for working with encoded polyline - data, such as reading and writing encoded values, as well as methods for normalizing and de-normalizing geographic - coordinates. It also provides validation utilities to ensure values conform to expected ranges for latitude and - longitude. -- h2: Methods -- api3: Denormalize(int, uint) - id: PolylineAlgorithm_PolylineEncoding_Denormalize_System_Int32_System_UInt32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L122 - metadata: - uid: PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int32,System.UInt32) - commentId: M:PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int32,System.UInt32) -- markdown: Converts a normalized integer coordinate value back to its floating-point representation based on the specified precision. -- code: public static double Denormalize(int value, uint precision = 5) -- h4: Parameters -- parameters: - - name: value - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: The integer value to denormalize. Typically produced by the method. - - name: precision - type: - - text: uint - url: https://learn.microsoft.com/dotnet/api/system.uint32 - description: The number of decimal places used during normalization. Default is 5, matching standard polyline encoding precision. - optional: true -- h4: Returns -- parameters: - - type: - - text: double - url: https://learn.microsoft.com/dotnet/api/system.double - description: The denormalized floating-point coordinate value. -- h4: Remarks -- markdown: >- -

- - This method reverses the normalization performed by . It takes an integer value and converts it - - to a double by dividing it by 10 raised to the power of the specified precision. If precision is 0, - - the value is returned as a double without division. - -

- -

- - The calculation is performed inside a checked block to ensure that any arithmetic overflow is detected - - and an is thrown. - -

- -

- - For example, with a precision of 5: - - -

  • A value of 3778903 becomes 37.78903
  • A value of -12241230 becomes -122.4123
- -

- -

- - If the input value is 0, the method returns 0.0 immediately. - -

-- h4: Exceptions -- parameters: - - type: - - text: OverflowException - url: https://learn.microsoft.com/dotnet/api/system.overflowexception - description: Thrown if the arithmetic operation overflows during conversion. -- api3: GetRequiredBufferSize(int) - id: PolylineAlgorithm_PolylineEncoding_GetRequiredBufferSize_System_Int32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L298 - metadata: - uid: PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(System.Int32) - commentId: M:PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(System.Int32) -- markdown: Calculates the number of characters required to encode a delta value in polyline format. -- code: public static int GetRequiredBufferSize(int delta) -- h4: Parameters -- parameters: - - name: delta - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: >- - The integer delta value to calculate the encoded size for. This value typically represents the difference between - - consecutive coordinate values in polyline encoding. -- h4: Returns -- parameters: - - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: The number of characters required to encode the specified delta value. The minimum return value is 1. -- h4: Remarks -- markdown: >- -

- - This method determines how many characters will be needed to represent an integer delta value when encoded - - using the polyline encoding algorithm. It performs the same zigzag encoding transformation as - - but only calculates the required buffer size without actually writing any data. - -

- -

- - The calculation process: - - -

  1. Applies zigzag encoding: left-shifts the value by 1 bit, then inverts all bits if the original value was negative
  2. Counts how many 5-bit chunks are needed to represent the encoded value
  3. Each chunk requires one character, with a minimum of 1 character for any value
- -

- -

- - This method is useful for pre-allocating buffers of the correct size before encoding polyline data, helping to avoid - - buffer overflow checks during the actual encoding process. - -

- -

- - The method uses a long internally to prevent overflow during the left-shift operation on large negative values. - -

-- h4: See Also -- list: - - - text: PolylineEncoding - url: PolylineAlgorithm.PolylineEncoding.html - - . - - text: TryWriteValue - url: PolylineAlgorithm.PolylineEncoding.html#PolylineAlgorithm_PolylineEncoding_TryWriteValue_System_Int32_System_Span_System_Char__System_Int32__ - - ( - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - - ',' - - " " - - text: Span - url: https://learn.microsoft.com/dotnet/api/system.span-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - - ',' - - " " - - ref - - " " - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - - ) -- api3: Normalize(double, uint) - id: PolylineAlgorithm_PolylineEncoding_Normalize_System_Double_System_UInt32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L61 - metadata: - uid: PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,System.UInt32) - commentId: M:PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,System.UInt32) -- markdown: Normalizes a geographic coordinate value to an integer representation based on the specified precision. -- code: public static int Normalize(double value, uint precision = 5) -- h4: Parameters -- parameters: - - name: value - type: - - text: double - url: https://learn.microsoft.com/dotnet/api/system.double - description: The numeric value to normalize. Must be a finite number (not NaN or infinity). - - name: precision - type: - - text: uint - url: https://learn.microsoft.com/dotnet/api/system.uint32 - description: >- - The number of decimal places of precision to preserve in the normalized value. - - The value is multiplied by 10^precision before rounding. - - Default is 5, which is standard for polyline encoding. - optional: true -- h4: Returns -- parameters: - - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: An integer representing the normalized value. Returns 0 if the input value is 0.0. -- h4: Remarks -- markdown: >- -

- - 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 truncating the result to an integer. - -

- -

- - For example, with the default precision of 5: - - -

  • A value of 37.78903 becomes 3778903
  • A value of -122.4123 becomes -12241230
- -

- -

- - The method validates that the input value is finite (not NaN or infinity) before performing normalization. - - If the precision is 0, the value is rounded without multiplication. - -

-- h4: Exceptions -- parameters: - - type: - - text: ArgumentOutOfRangeException - url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception - description: Thrown when value is not a finite number (NaN or infinity). - - type: - - text: OverflowException - url: https://learn.microsoft.com/dotnet/api/system.overflowexception - description: Thrown when the normalized result exceeds the range of a 32-bit signed integer during the conversion from double to int. -- api3: TryReadValue(ref int, ReadOnlyMemory, ref int) - id: PolylineAlgorithm_PolylineEncoding_TryReadValue_System_Int32__System_ReadOnlyMemory_System_Char__System_Int32__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L169 - metadata: - uid: PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int32@,System.ReadOnlyMemory{System.Char},System.Int32@) - commentId: M:PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int32@,System.ReadOnlyMemory{System.Char},System.Int32@) -- markdown: Attempts to read an encoded integer value from a polyline buffer, updating the specified delta and position. -- code: public static bool TryReadValue(ref int delta, ReadOnlyMemory buffer, ref int position) -- h4: Parameters -- parameters: - - name: delta - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: Reference to the integer accumulator that will be updated with the decoded value. - - name: buffer - type: - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: The buffer containing polyline-encoded characters. - - name: position - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: Reference to the current position in the buffer. This value is updated as characters are read. -- h4: Returns -- parameters: - - type: - - text: bool - url: https://learn.microsoft.com/dotnet/api/system.boolean - description: true if a value was successfully read and decoded; false if the buffer ended before a complete value was read. -- h4: Remarks -- markdown: >- -

- - This method decodes a value from a polyline-encoded character buffer, starting at the given position. It reads - - characters sequentially, applying the polyline decoding algorithm, and updates the delta with - - the decoded value. The position is advanced as characters are processed. - -

- -

- - The decoding process continues until a character with a value less than the algorithm's space constant is encountered, - - which signals the end of the encoded value. If the buffer is exhausted before a complete value is read, the method returns false. - -

- -

- - The decoded value is added to delta using zigzag decoding, which handles both positive and negative values. - -

-- api3: TryWriteValue(int, Span, ref int) - id: PolylineAlgorithm_PolylineEncoding_TryWriteValue_System_Int32_System_Span_System_Char__System_Int32__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L237 - metadata: - uid: PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int32,System.Span{System.Char},System.Int32@) - commentId: M:PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int32,System.Span{System.Char},System.Int32@) -- markdown: Attempts to write an encoded integer value to a polyline buffer, updating the specified position. -- code: public static bool TryWriteValue(int delta, Span buffer, ref int position) -- h4: Parameters -- parameters: - - name: delta - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: >- - The integer value to encode and write to the buffer. This value typically represents the difference between consecutive - - coordinate values in polyline encoding. - - name: buffer - type: - - text: Span - url: https://learn.microsoft.com/dotnet/api/system.span-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: The destination buffer where the encoded characters will be written. Must have sufficient capacity to hold the encoded value. - - name: position - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: >- - Reference to the current position in the buffer. This value is updated as characters are written to reflect the new position - - after encoding is complete. -- h4: Returns -- parameters: - - type: - - text: bool - url: https://learn.microsoft.com/dotnet/api/system.boolean - description: >- - true if the value was successfully encoded and written to the buffer; false if the buffer - - does not have sufficient remaining capacity to hold the encoded value. -- h4: Remarks -- markdown: >- -

- - This method encodes an integer delta value into a polyline-encoded format and writes it to the provided character buffer, - - starting at the given position. It applies zigzag encoding followed by the polyline encoding algorithm to represent - - both positive and negative values efficiently. - -

- -

- - The encoding process first converts the value using zigzag encoding (left shift by 1, with bitwise inversion for negative values), - - then writes it as a sequence of characters. Each character encodes 5 bits of data, with continuation bits indicating whether - - more characters follow. The position is advanced as characters are written. - -

- -

- - Before writing, the method validates that sufficient space is available in the buffer by calling . - - If the buffer does not have enough remaining capacity, the method returns false without modifying the buffer or position. - -

- -

- - This method is the inverse of and can be used to encode coordinate deltas for polyline serialization. - -

-- api3: ValidateBlockLength(ReadOnlySpan) - id: PolylineAlgorithm_PolylineEncoding_ValidateBlockLength_System_ReadOnlySpan_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L438 - metadata: - uid: PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan{System.Char}) - commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan{System.Char}) -- markdown: Validates the block structure of a polyline segment, ensuring each encoded value does not exceed 7 characters and the polyline ends correctly. -- code: public static void ValidateBlockLength(ReadOnlySpan polyline) -- h4: Parameters -- parameters: - - name: polyline - type: - - text: ReadOnlySpan - url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: A span representing the polyline segment to validate. -- h4: Remarks -- markdown: >- -

- - Iterates through the polyline, counting the length of each block (a sequence of characters representing an encoded value). - - Throws an if any block exceeds 7 characters or if the polyline does not end with a valid block terminator. - -

-- h4: Exceptions -- parameters: - - type: - - text: ArgumentException - url: https://learn.microsoft.com/dotnet/api/system.argumentexception - description: Thrown when a block exceeds 7 characters or the polyline does not end with a valid block terminator. -- api3: ValidateCharRange(ReadOnlySpan) - id: PolylineAlgorithm_PolylineEncoding_ValidateCharRange_System_ReadOnlySpan_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L392 - metadata: - uid: PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan{System.Char}) - commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan{System.Char}) -- markdown: Validates that all characters in the polyline segment are within the allowed ASCII range for polyline encoding. -- code: public static void ValidateCharRange(ReadOnlySpan polyline) -- h4: Parameters -- parameters: - - name: polyline - type: - - text: ReadOnlySpan - url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: A span representing the polyline segment to validate. -- h4: Remarks -- markdown: >- -

- - Uses SIMD vectorization for efficient validation of large spans. Falls back to scalar checks for any block where an invalid character is detected. - -

- -

- - The valid range is from '?' (63) to '_' (95), inclusive. If an invalid character is found, an is thrown. - -

-- h4: Exceptions -- parameters: - - type: - - text: ArgumentException - url: https://learn.microsoft.com/dotnet/api/system.argumentexception - description: Thrown when an invalid character is found in the polyline segment. -- api3: ValidateFormat(ReadOnlySpan) - id: PolylineAlgorithm_PolylineEncoding_ValidateFormat_System_ReadOnlySpan_System_Char__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L370 - metadata: - uid: PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan{System.Char}) - commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan{System.Char}) -- markdown: Validates the format of a polyline segment, ensuring all characters are valid and block structure is correct. -- code: public static void ValidateFormat(ReadOnlySpan polyline) -- h4: Parameters -- parameters: - - name: polyline - type: - - text: ReadOnlySpan - url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - description: A span representing the polyline segment to validate. -- h4: Remarks -- markdown: >- -

- - This method performs two levels of validation on the provided polyline segment: - -

- -
  1. - Character Range Validation: Checks that every character in the polyline is within the valid ASCII range for polyline encoding ('?' [63] to '_' [95], inclusive). - Uses SIMD acceleration for efficient validation of large segments. -
  2. - Block Structure Validation: Ensures that each encoded value (block) does not exceed 7 characters and that the polyline ends with a valid block terminator. -
-

- - If an invalid character or block structure is detected, an is thrown with details about the error. - -

-- h4: Exceptions -- parameters: - - type: - - text: ArgumentException - url: https://learn.microsoft.com/dotnet/api/system.argumentexception - description: Thrown when an invalid character is found or the block structure is invalid. -languageId: csharp -metadata: - description: >- - Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and de-normalizing - - geographic coordinate values. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions.yml deleted file mode 100644 index b257b79a..00000000 --- a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions.yml +++ /dev/null @@ -1,127 +0,0 @@ -### YamlMime:ApiPage -title: Class PolylineEncodingOptions -body: -- api1: Class PolylineEncodingOptions - id: PolylineAlgorithm_PolylineEncodingOptions - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L29 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptions - commentId: T:PolylineAlgorithm.PolylineEncodingOptions -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Provides configuration options for polyline encoding operations. -- code: public sealed class PolylineEncodingOptions -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Remarks -- markdown: >- -

- - This class allows you to configure various aspects of polyline encoding, including: - -

- -
  • The level for coordinate encoding
  • The for memory allocation strategy
  • The for diagnostic logging
- -

- - All properties have internal setters and should be configured through a builder or factory pattern. - -

-- h2: Properties -- api3: LoggerFactory - id: PolylineAlgorithm_PolylineEncodingOptions_LoggerFactory - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L41 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptions.LoggerFactory - commentId: P:PolylineAlgorithm.PolylineEncodingOptions.LoggerFactory -- markdown: Gets the logger factory used for diagnostic logging during encoding operations. -- code: public ILoggerFactory LoggerFactory { get; } -- h4: Property Value -- parameters: - - type: - - text: ILoggerFactory - url: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerfactory -- h4: Remarks -- markdown: >- - The default logger factory is , which does not log any messages. - - To enable logging, provide a custom implementation. -- api3: Precision - id: PolylineAlgorithm_PolylineEncodingOptions_Precision - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L60 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptions.Precision - commentId: P:PolylineAlgorithm.PolylineEncodingOptions.Precision -- markdown: Gets the precision level used for encoding coordinate values. -- code: public uint Precision { get; } -- h4: Property Value -- parameters: - - type: - - text: uint - url: https://learn.microsoft.com/dotnet/api/system.uint32 -- h4: Remarks -- markdown: >- -

- - The precision determines the number of decimal places to which each coordinate value (latitude or longitude) - - is multiplied and truncated (not rounded) before encoding. For example, a precision of 5 means each coordinate is multiplied by 10^5 - - and truncated to an integer before encoding. - -

- -

- - This setting does not directly correspond to a physical distance or accuracy in meters, but rather controls - - the granularity of the encoded values. - -

-- api3: StackAllocLimit - id: PolylineAlgorithm_PolylineEncodingOptions_StackAllocLimit - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L73 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptions.StackAllocLimit - commentId: P:PolylineAlgorithm.PolylineEncodingOptions.StackAllocLimit -- markdown: Gets the maximum buffer size (in characters) that can be allocated on the stack for encoding operations. -- code: public int StackAllocLimit { get; } -- h4: Property Value -- parameters: - - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 -- h4: Remarks -- markdown: >- - When the required buffer size for encoding exceeds this limit, memory will be allocated on the heap instead of the stack. - - This setting specifically applies to stack allocation of character arrays (stackalloc char[]) used during polyline encoding, - - balancing performance and stack safety. -languageId: csharp -metadata: - description: Provides configuration options for polyline encoding operations. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml deleted file mode 100644 index 291ae3d6..00000000 --- a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml +++ /dev/null @@ -1,141 +0,0 @@ -### YamlMime:ApiPage -title: Class PolylineEncodingOptionsBuilder -body: -- api1: Class PolylineEncodingOptionsBuilder - id: PolylineAlgorithm_PolylineEncodingOptionsBuilder - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L15 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder - commentId: T:PolylineAlgorithm.PolylineEncodingOptionsBuilder -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Provides a builder for configuring options for polyline encoding operations. -- code: public sealed class PolylineEncodingOptionsBuilder -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: PolylineEncodingOptionsBuilder - url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Methods -- api3: Build() - id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_Build - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L38 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.Build - commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.Build -- markdown: Builds a new instance using the configured options. -- code: public PolylineEncodingOptions Build() -- h4: Returns -- parameters: - - type: - - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html - description: A configured instance. -- api3: Create() - id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_Create - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L28 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.Create - commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.Create -- markdown: Creates a new instance for the specified coordinate type. -- code: public static PolylineEncodingOptionsBuilder Create() -- h4: Returns -- parameters: - - type: - - text: PolylineEncodingOptionsBuilder - url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html - description: An instance for configuring polyline encoding options. -- api3: WithLoggerFactory(ILoggerFactory) - id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithLoggerFactory_Microsoft_Extensions_Logging_ILoggerFactory_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L97 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithLoggerFactory(Microsoft.Extensions.Logging.ILoggerFactory) - commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithLoggerFactory(Microsoft.Extensions.Logging.ILoggerFactory) -- markdown: Configures the to be used for logging during polyline encoding operations. -- code: public PolylineEncodingOptionsBuilder WithLoggerFactory(ILoggerFactory loggerFactory) -- h4: Parameters -- parameters: - - name: loggerFactory - type: - - text: ILoggerFactory - url: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerfactory - description: The instance to use for logging. If null, a will be used instead. -- h4: Returns -- parameters: - - type: - - text: PolylineEncodingOptionsBuilder - url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html - description: The current instance for method chaining. -- api3: WithPrecision(uint) - id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithPrecision_System_UInt32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L82 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithPrecision(System.UInt32) - commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithPrecision(System.UInt32) -- markdown: Sets the coordinate encoding precision. -- code: public PolylineEncodingOptionsBuilder WithPrecision(uint precision) -- h4: Parameters -- parameters: - - name: precision - type: - - text: uint - url: https://learn.microsoft.com/dotnet/api/system.uint32 - description: The number of decimal places to use for encoding coordinate values. Default is 5. -- h4: Returns -- parameters: - - type: - - text: PolylineEncodingOptionsBuilder - url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html - description: The current instance for method chaining. -- api3: WithStackAllocLimit(int) - id: PolylineAlgorithm_PolylineEncodingOptionsBuilder_WithStackAllocLimit_System_Int32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptionsBuilder.cs#L61 - metadata: - uid: PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithStackAllocLimit(System.Int32) - commentId: M:PolylineAlgorithm.PolylineEncodingOptionsBuilder.WithStackAllocLimit(System.Int32) -- markdown: Configures the buffer size used for stack allocation during polyline encoding operations. -- code: public PolylineEncodingOptionsBuilder WithStackAllocLimit(int stackAllocLimit) -- h4: Parameters -- parameters: - - name: stackAllocLimit - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: The maximum buffer size to use for stack allocation. Must be greater than or equal to 1. -- h4: Returns -- parameters: - - type: - - text: PolylineEncodingOptionsBuilder - url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html - description: The current instance for method chaining. -- h4: Remarks -- markdown: This method allows customization of the internal buffer size for encoding, which can impact performance and memory usage. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentOutOfRangeException - url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception - description: Thrown if stackAllocLimit is less than 1. -languageId: csharp -metadata: - description: Provides a builder for configuring options for polyline encoding operations. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineFormatter.yml b/api-reference/0.0/PolylineAlgorithm.PolylineFormatter.yml deleted file mode 100644 index 8c385549..00000000 --- a/api-reference/0.0/PolylineAlgorithm.PolylineFormatter.yml +++ /dev/null @@ -1,168 +0,0 @@ -### YamlMime:ApiPage -title: Class PolylineFormatter -body: -- api1: Class PolylineFormatter - id: PolylineAlgorithm_PolylineFormatter - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L22 - metadata: - uid: PolylineAlgorithm.PolylineFormatter - commentId: T:PolylineAlgorithm.PolylineFormatter -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: >- - Provides static factory methods and ready-made instances of - - for the most common polyline surface types. -- code: public static class PolylineFormatter -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: PolylineFormatter - url: PolylineAlgorithm.PolylineFormatter.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.MemberwiseClone() - url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Remarks -- markdown: >- -

- - Use or for the two most common cases. - - Call to build a custom formatter from a pair of delegates. - -

-- h2: Properties -- api3: ForMemory - id: PolylineAlgorithm_PolylineFormatter_ForMemory - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L36 - metadata: - uid: PolylineAlgorithm.PolylineFormatter.ForMemory - commentId: P:PolylineAlgorithm.PolylineFormatter.ForMemory -- markdown: >- - Gets a pass-through formatter for of . - - Both Write and Read are identity operations. -- code: public static IPolylineFormatter> ForMemory { get; } -- h4: Property Value -- parameters: - - type: - - text: IPolylineFormatter - url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html - - < - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - - '>' -- api3: ForString - id: PolylineAlgorithm_PolylineFormatter_ForString - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L27 - metadata: - uid: PolylineAlgorithm.PolylineFormatter.ForString - commentId: P:PolylineAlgorithm.PolylineFormatter.ForString -- markdown: >- - Gets a formatter that produces a from the encoded char buffer and reads - - the buffer back via string.AsMemory(). -- code: public static IPolylineFormatter ForString { get; } -- h4: Property Value -- parameters: - - type: - - text: IPolylineFormatter - url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html - - < - - text: string - url: https://learn.microsoft.com/dotnet/api/system.string - - '>' -- h2: Methods -- api3: Create(Func, T>, Func>) - id: PolylineAlgorithm_PolylineFormatter_Create__1_System_Func_System_ReadOnlyMemory_System_Char____0__System_Func___0_System_ReadOnlyMemory_System_Char___ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L56 - metadata: - uid: PolylineAlgorithm.PolylineFormatter.Create``1(System.Func{System.ReadOnlyMemory{System.Char},``0},System.Func{``0,System.ReadOnlyMemory{System.Char}}) - commentId: M:PolylineAlgorithm.PolylineFormatter.Create``1(System.Func{System.ReadOnlyMemory{System.Char},``0},System.Func{``0,System.ReadOnlyMemory{System.Char}}) -- markdown: Creates a custom from a pair of delegates. -- code: public static IPolylineFormatter Create(Func, T> write, Func> read) -- h4: Parameters -- parameters: - - name: write - type: - - text: Func - url: https://learn.microsoft.com/dotnet/api/system.func-2 - - < - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - - ',' - - " " - - T - - '>' - description: >- - Converts the encoded of produced by the encoder - - into a T. - - name: read - type: - - text: Func - url: https://learn.microsoft.com/dotnet/api/system.func-2 - - < - - T - - ',' - - " " - - text: ReadOnlyMemory - url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 - - < - - text: char - url: https://learn.microsoft.com/dotnet/api/system.char - - '>' - - '>' - description: Extracts the encoded character buffer from a T for the decoder to consume. -- h4: Returns -- parameters: - - type: - - text: IPolylineFormatter - url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html - - < - - T - - '>' - description: A sealed backed by the supplied delegates. -- h4: Type Parameters -- parameters: - - name: T - description: The polyline surface type. -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: Thrown when write or read is null. -languageId: csharp -metadata: - description: >- - Provides static factory methods and ready-made instances of - - for the most common polyline surface types. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml b/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml deleted file mode 100644 index 4420f1d4..00000000 --- a/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml +++ /dev/null @@ -1,50 +0,0 @@ -### YamlMime:ApiPage -title: Delegate PolylineItemFactory -body: -- api1: Delegate PolylineItemFactory - id: PolylineAlgorithm_PolylineItemFactory_1 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineItemFactory.cs#L20 - metadata: - uid: PolylineAlgorithm.PolylineItemFactory`1 - commentId: T:PolylineAlgorithm.PolylineItemFactory`1 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: >- - Represents a factory method that reconstructs a T item from an array of - - scaled integer values decoded from a polyline. -- code: public delegate T PolylineItemFactory(ReadOnlySpan values) -- h4: Parameters -- parameters: - - name: values - type: - - text: ReadOnlySpan - url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 - - < - - text: long - url: https://learn.microsoft.com/dotnet/api/system.int64 - - '>' - description: >- - The scaled integer values accumulated from the polyline decoder. Each element corresponds to one - - column as defined by the that built the associated formatter. -- h4: Returns -- parameters: - - type: - - T - description: A T instance reconstructed from values. -- h4: Type Parameters -- parameters: - - name: T - description: The coordinate or item type to create. -languageId: csharp -metadata: - description: >- - Represents a factory method that reconstructs a T item from an array of - - scaled integer values decoded from a polyline. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml b/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml deleted file mode 100644 index b7dade96..00000000 --- a/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml +++ /dev/null @@ -1,157 +0,0 @@ -### YamlMime:ApiPage -title: Class PolylineOptions -body: -- api1: Class PolylineOptions - id: PolylineAlgorithm_PolylineOptions_2 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L22 - metadata: - uid: PolylineAlgorithm.PolylineOptions`2 - commentId: T:PolylineAlgorithm.PolylineOptions`2 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: Provides unified configuration for a formatter-driven encoding or decoding operation. -- code: public sealed class PolylineOptions -- h4: Type Parameters -- parameters: - - name: TValue - description: The coordinate or item type understood by the value formatter. - - name: TPolyline - description: The polyline surface type understood by the polyline formatter. -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: PolylineOptions - url: PolylineAlgorithm.PolylineOptions-2.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Remarks -- markdown: >- - Combines an (which defines the column schema, scaling - - rules, and item factory) with an (which converts between - - the raw character buffer and the surface type) and a (which - - controls buffer sizes, precision for legacy paths, and logging). -- h2: Constructors -- api3: PolylineOptions(IPolylineValueFormatter, IPolylineFormatter, PolylineEncodingOptions?) - id: PolylineAlgorithm_PolylineOptions_2__ctor_PolylineAlgorithm_Abstraction_IPolylineValueFormatter__0__PolylineAlgorithm_Abstraction_IPolylineFormatter__1__PolylineAlgorithm_PolylineEncodingOptions_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L42 - metadata: - uid: PolylineAlgorithm.PolylineOptions`2.#ctor(PolylineAlgorithm.Abstraction.IPolylineValueFormatter{`0},PolylineAlgorithm.Abstraction.IPolylineFormatter{`1},PolylineAlgorithm.PolylineEncodingOptions) - commentId: M:PolylineAlgorithm.PolylineOptions`2.#ctor(PolylineAlgorithm.Abstraction.IPolylineValueFormatter{`0},PolylineAlgorithm.Abstraction.IPolylineFormatter{`1},PolylineAlgorithm.PolylineEncodingOptions) -- markdown: Initializes a new instance of . -- code: public PolylineOptions(IPolylineValueFormatter valueFormatter, IPolylineFormatter polylineFormatter, PolylineEncodingOptions? encoding = null) -- h4: Parameters -- parameters: - - name: valueFormatter - type: - - text: IPolylineValueFormatter - url: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.html - - < - - TValue - - '>' - description: >- - The formatter that defines the column schema, scaling rules, and item factory. Must not be - - null. - - name: polylineFormatter - type: - - text: IPolylineFormatter - url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html - - < - - TPolyline - - '>' - description: >- - The formatter that converts between the raw character buffer and - - TPolyline. Must not be null. - - name: encoding - type: - - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html - - '?' - description: >- - The encoding options that control buffer sizes, precision, and logging. - - Pass null to use default options. - optional: true -- h4: Exceptions -- parameters: - - type: - - text: ArgumentNullException - url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception - description: >- - Thrown when valueFormatter or polylineFormatter is - - null. -- h2: Properties -- api3: Encoding - id: PolylineAlgorithm_PolylineOptions_2_Encoding - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L73 - metadata: - uid: PolylineAlgorithm.PolylineOptions`2.Encoding - commentId: P:PolylineAlgorithm.PolylineOptions`2.Encoding -- markdown: Gets the encoding options that control buffer sizes, precision, and logging. -- code: public PolylineEncodingOptions Encoding { get; } -- h4: Property Value -- parameters: - - type: - - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html -- api3: PolylineFormatter - id: PolylineAlgorithm_PolylineOptions_2_PolylineFormatter - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L68 - metadata: - uid: PolylineAlgorithm.PolylineOptions`2.PolylineFormatter - commentId: P:PolylineAlgorithm.PolylineOptions`2.PolylineFormatter -- markdown: >- - Gets the formatter that converts between the raw character buffer and - - TPolyline. -- code: public IPolylineFormatter PolylineFormatter { get; } -- h4: Property Value -- parameters: - - type: - - text: IPolylineFormatter - url: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.html - - < - - TPolyline - - '>' -- api3: ValueFormatter - id: PolylineAlgorithm_PolylineOptions_2_ValueFormatter - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L62 - metadata: - uid: PolylineAlgorithm.PolylineOptions`2.ValueFormatter - commentId: P:PolylineAlgorithm.PolylineOptions`2.ValueFormatter -- markdown: Gets the formatter that defines the column schema, scaling rules, and item factory. -- code: public IPolylineValueFormatter ValueFormatter { get; } -- h4: Property Value -- parameters: - - type: - - text: IPolylineValueFormatter - url: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.html - - < - - TValue - - '>' -languageId: csharp -metadata: - description: Provides unified configuration for a formatter-driven encoding or decoding operation. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineValueFormatter-1.yml b/api-reference/0.0/PolylineAlgorithm.PolylineValueFormatter-1.yml deleted file mode 100644 index 82de745a..00000000 --- a/api-reference/0.0/PolylineAlgorithm.PolylineValueFormatter-1.yml +++ /dev/null @@ -1,226 +0,0 @@ -### YamlMime:ApiPage -title: Class PolylineValueFormatter -body: -- api1: Class PolylineValueFormatter - id: PolylineAlgorithm_PolylineValueFormatter_1 - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L29 - metadata: - uid: PolylineAlgorithm.PolylineValueFormatter`1 - commentId: T:PolylineAlgorithm.PolylineValueFormatter`1 -- facts: - - name: Namespace - value: - text: PolylineAlgorithm - url: PolylineAlgorithm.html - - name: Assembly - value: PolylineAlgorithm.dll -- markdown: >- - Provides an immutable, sealed rule engine that describes how to extract and scale values from - - an object of type T for polyline encoding, and how to reconstruct an - - object of type T from scaled values during decoding. -- code: 'public sealed class PolylineValueFormatter : IPolylineValueFormatter' -- h4: Type Parameters -- parameters: - - name: T - description: The source object type from which column values are extracted or to which they are reconstructed. -- h4: Inheritance -- inheritance: - - text: object - url: https://learn.microsoft.com/dotnet/api/system.object - - text: PolylineValueFormatter - url: PolylineAlgorithm.PolylineValueFormatter-1.html -- h4: Implements -- list: - - text: IPolylineValueFormatter - url: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.html -- h4: Inherited Members -- list: - - text: object.Equals(object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) - - text: object.Equals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) - - text: object.GetHashCode() - url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode - - text: object.GetType() - url: https://learn.microsoft.com/dotnet/api/system.object.gettype - - text: object.ReferenceEquals(object, object) - url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals - - text: object.ToString() - url: https://learn.microsoft.com/dotnet/api/system.object.tostring -- h2: Remarks -- markdown: >- -

- - Instances of this class are constructed exclusively through . - -

- -

- - The sealed modifier allows the JIT to devirtualise and inline calls to - - and , eliminating vtable dispatch in the - - encoding/decoding hot loop. - -

-- h2: Properties -- api3: CanCreateItem - id: PolylineAlgorithm_PolylineValueFormatter_1_CanCreateItem - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L68 - metadata: - uid: PolylineAlgorithm.PolylineValueFormatter`1.CanCreateItem - commentId: P:PolylineAlgorithm.PolylineValueFormatter`1.CanCreateItem -- markdown: >- - Gets a value indicating whether a factory delegate was supplied at build time. - - When false, calling throws an - - . -- code: public bool CanCreateItem { get; } -- h4: Property Value -- parameters: - - type: - - text: bool - url: https://learn.microsoft.com/dotnet/api/system.boolean -- api3: HasBaselines - id: PolylineAlgorithm_PolylineValueFormatter_1_HasBaselines - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L61 - metadata: - uid: PolylineAlgorithm.PolylineValueFormatter`1.HasBaselines - commentId: P:PolylineAlgorithm.PolylineValueFormatter`1.HasBaselines -- markdown: >- - Gets a value indicating whether any column has a baseline defined. - - When false the encoder can skip the baseline-subtraction branch entirely, - - keeping the common-case encoding path branch-free. -- code: public bool HasBaselines { get; } -- h4: Property Value -- parameters: - - type: - - text: bool - url: https://learn.microsoft.com/dotnet/api/system.boolean -- api3: Width - id: PolylineAlgorithm_PolylineValueFormatter_1_Width - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L54 - metadata: - uid: PolylineAlgorithm.PolylineValueFormatter`1.Width - commentId: P:PolylineAlgorithm.PolylineValueFormatter`1.Width -- markdown: >- - Gets the number of columns (values per item). - - This is the required length of the passed to - - and the length of the span received by . -- code: public int Width { get; } -- h4: Property Value -- parameters: - - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 -- h2: Methods -- api3: CreateItem(ReadOnlySpan) - id: PolylineAlgorithm_PolylineValueFormatter_1_CreateItem_System_ReadOnlySpan_System_Int64__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L108 - metadata: - uid: PolylineAlgorithm.PolylineValueFormatter`1.CreateItem(System.ReadOnlySpan{System.Int64}) - commentId: M:PolylineAlgorithm.PolylineValueFormatter`1.CreateItem(System.ReadOnlySpan{System.Int64}) -- markdown: Reconstructs a T from the given scaled integer values. -- code: public T CreateItem(ReadOnlySpan values) -- h4: Parameters -- parameters: - - name: values - type: - - text: ReadOnlySpan - url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 - - < - - text: long - url: https://learn.microsoft.com/dotnet/api/system.int64 - - '>' - description: The accumulated scaled integer values decoded from the polyline. -- h4: Returns -- parameters: - - type: - - T - description: A T reconstructed from values. -- h4: Exceptions -- parameters: - - type: - - text: InvalidOperationException - url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception - description: Thrown when no factory delegate was supplied via . -- api3: GetBaseline(int) - id: PolylineAlgorithm_PolylineValueFormatter_1_GetBaseline_System_Int32_ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L128 - metadata: - uid: PolylineAlgorithm.PolylineValueFormatter`1.GetBaseline(System.Int32) - commentId: M:PolylineAlgorithm.PolylineValueFormatter`1.GetBaseline(System.Int32) -- markdown: >- - Returns the baseline for the column at index, or 0 if none is configured. - - The encoder subtracts this value from the first item's scaled column value during encoding. -- code: public long GetBaseline(int index) -- h4: Parameters -- parameters: - - name: index - type: - - text: int - url: https://learn.microsoft.com/dotnet/api/system.int32 - description: >- - The zero-based column index. Must be in the range [0, ). - - An is thrown if the index is out of range. -- h4: Returns -- parameters: - - type: - - text: long - url: https://learn.microsoft.com/dotnet/api/system.int64 - description: The baseline value, or 0 when no baseline has been defined for the column. -- api3: GetValues(T, Span) - id: PolylineAlgorithm_PolylineValueFormatter_1_GetValues__0_System_Span_System_Int64__ - src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineValueFormatter.cs#L83 - metadata: - uid: PolylineAlgorithm.PolylineValueFormatter`1.GetValues(`0,System.Span{System.Int64}) - commentId: M:PolylineAlgorithm.PolylineValueFormatter`1.GetValues(`0,System.Span{System.Int64}) -- markdown: >- - Extracts and scales all column values from item into the values span. - - Called once per item in the encoding hot loop. This method performs no heap allocation; - - the caller is responsible for providing and owning the output buffer. -- code: public void GetValues(T item, Span values) -- h4: Parameters -- parameters: - - name: item - type: - - T - description: The source item from which column values are extracted. - - name: values - type: - - text: Span - url: https://learn.microsoft.com/dotnet/api/system.span-1 - - < - - text: long - url: https://learn.microsoft.com/dotnet/api/system.int64 - - '>' - description: >- - Output buffer that receives the scaled values. - - Its length must equal . -- h4: Exceptions -- parameters: - - type: - - text: ArgumentException - url: https://learn.microsoft.com/dotnet/api/system.argumentexception - description: Thrown when values.Length does not equal . -languageId: csharp -metadata: - description: >- - Provides an immutable, sealed rule engine that describes how to extract and scale values from - - an object of type T for polyline encoding, and how to reconstruct an - - object of type T from scaled values during decoding. diff --git a/api-reference/0.0/PolylineAlgorithm.yml b/api-reference/0.0/PolylineAlgorithm.yml deleted file mode 100644 index ad19be2d..00000000 --- a/api-reference/0.0/PolylineAlgorithm.yml +++ /dev/null @@ -1,71 +0,0 @@ -### YamlMime:ApiPage -title: Namespace PolylineAlgorithm -body: -- api1: Namespace PolylineAlgorithm - id: PolylineAlgorithm - metadata: - uid: PolylineAlgorithm - commentId: N:PolylineAlgorithm -- h3: Namespaces -- parameters: - - type: - text: PolylineAlgorithm.Abstraction - url: PolylineAlgorithm.Abstraction.html - - type: - text: PolylineAlgorithm.Extensions - url: PolylineAlgorithm.Extensions.html -- h3: Classes -- parameters: - - type: - text: FormatterBuilder - url: PolylineAlgorithm.FormatterBuilder-1.html - description: Provides a fluent builder for constructing a . - - type: - text: InvalidPolylineException - url: PolylineAlgorithm.InvalidPolylineException.html - description: Exception thrown when a polyline is determined to be malformed or invalid during processing. - - type: - text: PolylineEncoding - url: PolylineAlgorithm.PolylineEncoding.html - description: >- - Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and de-normalizing - - geographic coordinate values. - - type: - text: PolylineEncodingOptions - url: PolylineAlgorithm.PolylineEncodingOptions.html - description: Provides configuration options for polyline encoding operations. - - type: - text: PolylineEncodingOptionsBuilder - url: PolylineAlgorithm.PolylineEncodingOptionsBuilder.html - description: Provides a builder for configuring options for polyline encoding operations. - - type: - text: PolylineFormatter - url: PolylineAlgorithm.PolylineFormatter.html - description: >- - Provides static factory methods and ready-made instances of - - for the most common polyline surface types. - - type: - text: PolylineOptions - url: PolylineAlgorithm.PolylineOptions-2.html - description: Provides unified configuration for a formatter-driven encoding or decoding operation. - - type: - text: PolylineValueFormatter - url: PolylineAlgorithm.PolylineValueFormatter-1.html - description: >- - Provides an immutable, sealed rule engine that describes how to extract and scale values from - - an object of type T for polyline encoding, and how to reconstruct an - - object of type T from scaled values during decoding. -- h3: Delegates -- parameters: - - type: - text: PolylineItemFactory - url: PolylineAlgorithm.PolylineItemFactory-1.html - description: >- - Represents a factory method that reconstructs a T item from an array of - - scaled integer values decoded from a polyline. -languageId: csharp diff --git a/api-reference/0.0/toc.yml b/api-reference/0.0/toc.yml deleted file mode 100644 index 2004a401..00000000 --- a/api-reference/0.0/toc.yml +++ /dev/null @@ -1,49 +0,0 @@ -### YamlMime:TableOfContent -- name: PolylineAlgorithm - href: PolylineAlgorithm.yml - items: - - name: Classes - - name: FormatterBuilder - href: PolylineAlgorithm.FormatterBuilder-1.yml - - name: InvalidPolylineException - href: PolylineAlgorithm.InvalidPolylineException.yml - - name: PolylineEncoding - href: PolylineAlgorithm.PolylineEncoding.yml - - name: PolylineEncodingOptions - href: PolylineAlgorithm.PolylineEncodingOptions.yml - - name: PolylineEncodingOptionsBuilder - href: PolylineAlgorithm.PolylineEncodingOptionsBuilder.yml - - name: PolylineFormatter - href: PolylineAlgorithm.PolylineFormatter.yml - - name: PolylineOptions - href: PolylineAlgorithm.PolylineOptions-2.yml - - name: PolylineValueFormatter - href: PolylineAlgorithm.PolylineValueFormatter-1.yml - - name: Delegates - - name: PolylineItemFactory - href: PolylineAlgorithm.PolylineItemFactory-1.yml -- name: PolylineAlgorithm.Abstraction - href: PolylineAlgorithm.Abstraction.yml - items: - - name: Classes - - name: AbstractPolylineDecoder - href: PolylineAlgorithm.Abstraction.AbstractPolylineDecoder-2.yml - - name: AbstractPolylineEncoder - href: PolylineAlgorithm.Abstraction.AbstractPolylineEncoder-2.yml - - name: Interfaces - - name: IPolylineDecoder - href: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml - - name: IPolylineEncoder - href: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml - - name: IPolylineFormatter - href: PolylineAlgorithm.Abstraction.IPolylineFormatter-1.yml - - name: IPolylineValueFormatter - href: PolylineAlgorithm.Abstraction.IPolylineValueFormatter-1.yml -- name: PolylineAlgorithm.Extensions - href: PolylineAlgorithm.Extensions.yml - items: - - name: Classes - - name: PolylineDecoderExtensions - href: PolylineAlgorithm.Extensions.PolylineDecoderExtensions.yml - - name: PolylineEncoderExtensions - href: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index 15041b6e..7dc5c581 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -1,58 +1 @@ #nullable enable -PolylineAlgorithm.Abstraction.IPolylineFormatter -PolylineAlgorithm.Abstraction.IPolylineFormatter.CreateItem(System.ReadOnlySpan values) -> TCoordinate -PolylineAlgorithm.Abstraction.IPolylineFormatter.GetBaseline(int index) -> long -PolylineAlgorithm.Abstraction.IPolylineFormatter.GetValues(TCoordinate item, System.Span values) -> void -PolylineAlgorithm.Abstraction.IPolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory -PolylineAlgorithm.Abstraction.IPolylineFormatter.Width.get -> int -PolylineAlgorithm.Abstraction.IPolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline -PolylineAlgorithm.Abstraction.IPolylineDecoder -PolylineAlgorithm.Abstraction.IPolylineDecoder.Decode(TPolyline polyline, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable! -PolylineAlgorithm.Abstraction.IPolylineEncoder -PolylineAlgorithm.Abstraction.IPolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline -PolylineAlgorithm.FormatterBuilder -PolylineAlgorithm.FormatterBuilder.AddValue(string! name, System.Func! selector, uint precision = 5) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.Build() -> PolylineAlgorithm.PolylineFormatter! -PolylineAlgorithm.FormatterBuilder.ForPolyline(System.Func, TPolyline>! write, System.Func>! read) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.SetBaseline(long baseline) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.WithCreate(PolylineAlgorithm.PolylineItemFactory! create) -> PolylineAlgorithm.FormatterBuilder! -static PolylineAlgorithm.FormatterBuilder.Create() -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.PolylineFormatter -PolylineAlgorithm.PolylineFormatter.CreateItem(System.ReadOnlySpan values) -> TCoordinate -PolylineAlgorithm.PolylineFormatter.GetBaseline(int index) -> long -PolylineAlgorithm.PolylineFormatter.GetValues(TCoordinate item, System.Span values) -> void -PolylineAlgorithm.PolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory -PolylineAlgorithm.PolylineFormatter.Width.get -> int -PolylineAlgorithm.PolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline -PolylineAlgorithm.PolylineItemFactory -virtual PolylineAlgorithm.PolylineItemFactory.Invoke(System.ReadOnlySpan values) -> T -PolylineAlgorithm.PolylineOptions -PolylineAlgorithm.PolylineOptions.Formatter.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! -PolylineAlgorithm.PolylineOptions.LoggerFactory.get -> Microsoft.Extensions.Logging.ILoggerFactory! -PolylineAlgorithm.PolylineOptions.PolylineOptions(PolylineAlgorithm.Abstraction.IPolylineFormatter! formatter, int stackAllocLimit = 512, Microsoft.Extensions.Logging.ILoggerFactory? loggerFactory = null) -> void -PolylineAlgorithm.PolylineOptions.StackAllocLimit.get -> int -PolylineAlgorithm.PolylineDecoder -PolylineAlgorithm.PolylineDecoder.Decode(TPolyline polyline, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable! -PolylineAlgorithm.PolylineDecoder.PolylineDecoder(PolylineAlgorithm.PolylineOptions! options) -> void -PolylineAlgorithm.PolylineEncoder -PolylineAlgorithm.PolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline -PolylineAlgorithm.PolylineEncoder.PolylineEncoder(PolylineAlgorithm.PolylineOptions! options) -> void -PolylineAlgorithm.Extensions.PolylineDecoderExtensions -static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder! decoder, char[]! polyline) -> System.Collections.Generic.IEnumerable! -static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder! decoder, System.ReadOnlyMemory polyline) -> System.Collections.Generic.IEnumerable! -static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder, TValue>! decoder, string! polyline) -> System.Collections.Generic.IEnumerable! -PolylineAlgorithm.Extensions.PolylineEncoderExtensions -static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IPolylineEncoder! encoder, System.Collections.Generic.List! coordinates) -> TPolyline -static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IPolylineEncoder! encoder, TCoordinate[]! coordinates) -> TPolyline -PolylineAlgorithm.InvalidPolylineException -PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException() -> void -PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException(string! message, System.Exception! innerException) -> void -PolylineAlgorithm.PolylineEncoding -static PolylineAlgorithm.PolylineEncoding.Normalize(double value, uint precision = 5) -> int -static PolylineAlgorithm.PolylineEncoding.Denormalize(int value, uint precision = 5) -> double -static PolylineAlgorithm.PolylineEncoding.TryReadValue(ref int delta, System.ReadOnlyMemory buffer, ref int position) -> bool -static PolylineAlgorithm.PolylineEncoding.TryWriteValue(int delta, System.Span buffer, ref int position) -> bool -static PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(int delta) -> int -static PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan polyline) -> void -static PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan polyline) -> void -static PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan polyline) -> void From c375141375d83ff5b239810db22451791ada9cc3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:43:36 +0000 Subject: [PATCH 13/22] change int to long for accumulated, Normalize, Denormalize, TryReadValue/WriteValue, GetRequiredBufferSize; use ulong for zigzag arithmetic; update block max length to 13 Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/b16b1bac-62fc-44de-9c08-88c566cf476e Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- src/PolylineAlgorithm/Internal/Defaults.cs | 2 +- src/PolylineAlgorithm/PolylineDecoder.cs | 9 +-- src/PolylineAlgorithm/PolylineEncoder.cs | 2 +- src/PolylineAlgorithm/PolylineEncoding.cs | 46 ++++++------ .../PolylineEncodingTests.cs | 73 ++++++++++--------- 5 files changed, 63 insertions(+), 69 deletions(-) diff --git a/src/PolylineAlgorithm/Internal/Defaults.cs b/src/PolylineAlgorithm/Internal/Defaults.cs index 44a734fa..429676bd 100644 --- a/src/PolylineAlgorithm/Internal/Defaults.cs +++ b/src/PolylineAlgorithm/Internal/Defaults.cs @@ -111,7 +111,7 @@ internal static class Length { /// /// The maximum number of characters allowed to represent an encoded value. /// - internal const int Max = 7; + internal const int Max = 13; } } } diff --git a/src/PolylineAlgorithm/PolylineDecoder.cs b/src/PolylineAlgorithm/PolylineDecoder.cs index b242684e..46e0c190 100644 --- a/src/PolylineAlgorithm/PolylineDecoder.cs +++ b/src/PolylineAlgorithm/PolylineDecoder.cs @@ -93,8 +93,7 @@ public IEnumerable Decode(TPolyline polyline, CancellationToken can } int width = _formatter.Width; - int[] accumulated = new int[width]; - long[] longValues = new long[width]; + long[] accumulated = new long[width]; int position = 0; try { @@ -109,11 +108,7 @@ public IEnumerable Decode(TPolyline polyline, CancellationToken can } } - for (int j = 0; j < width; j++) { - longValues[j] = accumulated[j]; - } - - yield return _formatter.CreateItem(longValues.AsSpan()); + yield return _formatter.CreateItem(accumulated.AsSpan()); } } finally { _logger.LogOperationFinishedDebug(OperationName); diff --git a/src/PolylineAlgorithm/PolylineEncoder.cs b/src/PolylineAlgorithm/PolylineEncoder.cs index 3386f153..0fccb6ae 100644 --- a/src/PolylineAlgorithm/PolylineEncoder.cs +++ b/src/PolylineAlgorithm/PolylineEncoder.cs @@ -110,7 +110,7 @@ public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken long delta = current - previous[j]; previous[j] = current; - if (!PolylineEncoding.TryWriteValue((int)delta, buffer, ref position)) { + if (!PolylineEncoding.TryWriteValue(delta, buffer, ref position)) { _logger.LogOperationFailedDebug(OperationName); _logger.LogCannotWriteValueToBufferWarning(position, i); ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer(); diff --git a/src/PolylineAlgorithm/PolylineEncoding.cs b/src/PolylineAlgorithm/PolylineEncoding.cs index 3d6de5e7..f91af382 100644 --- a/src/PolylineAlgorithm/PolylineEncoding.cs +++ b/src/PolylineAlgorithm/PolylineEncoding.cs @@ -50,15 +50,12 @@ public static class PolylineEncoding { /// Default is 5, which is standard for polyline encoding. /// /// - /// An integer representing the normalized value. Returns 0 if the input is 0.0. + /// A long integer representing the normalized value. Returns 0 if the input is 0.0. /// /// /// Thrown when is not a finite number (NaN or infinity). /// - /// - /// Thrown when the normalized result exceeds the range of a 32-bit signed integer during the conversion from double to int. - /// - public static int Normalize(double value, uint precision = 5) { + public static long Normalize(double value, uint precision = 5) { // Fast return if the value is zero, return 0 as the normalized value. if (value.Equals(default)) { return 0; @@ -69,9 +66,9 @@ public static int Normalize(double value, uint precision = 5) { ExceptionGuard.ThrowNotFiniteNumber(nameof(value)); } - // Fast return if precision is zero, return current value converted to Int32. + // Fast return if precision is zero, return current value converted to Int64. if (precision == default) { - return (int)Math.Truncate(value); + return (long)Math.Truncate(value); } uint factor = Pow10.GetFactor(precision); @@ -79,7 +76,7 @@ public static int Normalize(double value, uint precision = 5) { const double Epsilon = 1e-9; checked { - return (int)Math.Truncate((value * factor) + (Epsilon * Math.Sign(value))); + return (long)Math.Truncate((value * factor) + (Epsilon * Math.Sign(value))); } } @@ -108,7 +105,7 @@ public static int Normalize(double value, uint precision = 5) { /// /// /// - /// The integer value to denormalize. Typically produced by the method. + /// The long integer value to denormalize. Typically produced by the method. /// /// /// The number of decimal places used during normalization. Default is 5, matching standard polyline encoding precision. @@ -119,7 +116,7 @@ public static int Normalize(double value, uint precision = 5) { /// /// Thrown if the arithmetic operation overflows during conversion. /// - public static double Denormalize(int value, uint precision = 5) { + public static double Denormalize(long value, uint precision = 5) { if (value.Equals(default)) { return default; } @@ -155,7 +152,7 @@ public static double Denormalize(int value, uint precision = 5) { /// /// /// - /// Reference to the integer accumulator that will be updated with the decoded value. + /// Reference to the long accumulator that will be updated with the decoded value. /// /// /// The buffer containing polyline-encoded characters. @@ -166,7 +163,7 @@ public static double Denormalize(int value, uint precision = 5) { /// /// if a value was successfully read and decoded; if the buffer ended before a complete value was read. /// - public static bool TryReadValue(ref int delta, ReadOnlyMemory buffer, ref int position) { + public static bool TryReadValue(ref long delta, ReadOnlyMemory buffer, ref int position) { // Validate that the position is within the bounds of the buffer. if (position >= buffer.Length) { return false; @@ -174,14 +171,14 @@ public static bool TryReadValue(ref int delta, ReadOnlyMemory buffer, ref // Initialize variables for reading the value. int chunk = 0; - int sum = 0; + ulong sum = 0; int shifter = 0; ReadOnlySpan span = buffer.Span; // Read characters from the buffer until a termination condition is met or the end of the buffer is reached. while (position < buffer.Length) { chunk = span[position++] - Defaults.Algorithm.QuestionMark; - sum |= (chunk & Defaults.Algorithm.UnitSeparator) << shifter; + sum |= (ulong)(chunk & Defaults.Algorithm.UnitSeparator) << shifter; shifter += Defaults.Algorithm.ShiftLength; // If the chunk is less than the space character, it indicates the end of the value. @@ -190,7 +187,7 @@ public static bool TryReadValue(ref int delta, ReadOnlyMemory buffer, ref } } - delta += (sum & 1) == 1 ? ~(sum >> 1) : sum >> 1; + delta += (sum & 1) == 1 ? (long)~(sum >> 1) : (long)(sum >> 1); // If the end of the buffer was reached without reading a complete value, return false. return chunk < Defaults.Algorithm.Space; @@ -220,7 +217,7 @@ public static bool TryReadValue(ref int delta, ReadOnlyMemory buffer, ref /// /// /// - /// The integer value to encode and write to the buffer. This value typically represents the difference between consecutive + /// The long value to encode and write to the buffer. This value typically represents the difference between consecutive /// coordinate values in polyline encoding. /// /// @@ -234,13 +231,13 @@ public static bool TryReadValue(ref int delta, ReadOnlyMemory buffer, ref /// if the value was successfully encoded and written to the buffer; if the buffer /// does not have sufficient remaining capacity to hold the encoded value. /// - public static bool TryWriteValue(int delta, Span buffer, ref int position) { + public static bool TryWriteValue(long delta, Span buffer, ref int position) { // Validate that the position and required space for write is within the bounds of the buffer. if (buffer[position..].Length < GetRequiredBufferSize(delta)) { return false; } - int rem = delta << 1; + ulong rem = (ulong)(delta << 1); // If the delta is negative, we need to invert the bits to get the correct representation. if (delta < 0) { @@ -248,7 +245,7 @@ public static bool TryWriteValue(int delta, Span buffer, ref int position) } // Write the value to the buffer in a way that encodes it using the specified algorithm. - while (rem >= Defaults.Algorithm.Space) { + while (rem >= (ulong)Defaults.Algorithm.Space) { buffer[position++] = (char)((Defaults.Algorithm.Space | (rem & Defaults.Algorithm.UnitSeparator)) @@ -284,19 +281,20 @@ public static bool TryWriteValue(int delta, Span buffer, ref int position) /// buffer overflow checks during the actual encoding process. /// /// - /// The method uses a internally to prevent overflow during the left-shift operation on large negative values. + /// The method uses internally to handle the full range of 64-bit signed values correctly + /// during the zigzag encoding step. /// /// /// - /// The integer delta value to calculate the encoded size for. This value typically represents the difference between + /// The long delta value to calculate the encoded size for. This value typically represents the difference between /// consecutive coordinate values in polyline encoding. /// /// /// The number of characters required to encode the specified delta value. The minimum return value is 1. /// /// - public static int GetRequiredBufferSize(int delta) { - long rem = (long)delta << 1; + public static int GetRequiredBufferSize(long delta) { + ulong rem = (ulong)(delta << 1); if (delta < 0) { rem = ~rem; @@ -304,7 +302,7 @@ public static int GetRequiredBufferSize(int delta) { int size = 1; - while (rem >= Defaults.Algorithm.Space) { + while (rem >= (ulong)Defaults.Algorithm.Space) { rem >>= Defaults.Algorithm.ShiftLength; size++; } diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs b/tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs index d598e3c2..72748b0a 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs @@ -20,10 +20,10 @@ public sealed class PolylineEncodingTests { [TestMethod] public void Normalize_With_Zero_Value_Returns_Zero() { // Act - int result = PolylineEncoding.Normalize(0.0); + long result = PolylineEncoding.Normalize(0.0); // Assert - Assert.AreEqual(0, result); + Assert.AreEqual(0L, result); } /// @@ -51,9 +51,9 @@ public void Normalize_With_NonFinite_Value_Throws_ArgumentOutOfRangeException(do [DataRow(37.789999, 5u, 3778999)] [DataRow(0.00001, 5u, 1)] [DataRow(-0.00001, 5u, -1)] - public void Normalize_With_Value_And_Precision_Returns_Expected_Normalized_Value(double value, uint precision, int expected) { + public void Normalize_With_Value_And_Precision_Returns_Expected_Normalized_Value(double value, uint precision, long expected) { // Act - int result = PolylineEncoding.Normalize(value, precision); + long result = PolylineEncoding.Normalize(value, precision); // Assert Assert.AreEqual(expected, result); @@ -87,7 +87,7 @@ public void Denormalize_With_Zero_Value_Returns_Zero() { [DataRow(37789034, 6u, 37.789034)] [DataRow(1, 5u, 0.00001)] [DataRow(-1, 5u, -0.00001)] - public void Denormalize_With_Value_And_Precision_Returns_Expected_Denormalized_Value(int value, uint precision, double expected) { + public void Denormalize_With_Value_And_Precision_Returns_Expected_Denormalized_Value(long value, uint precision, double expected) { // Act double result = PolylineEncoding.Denormalize(value, precision); @@ -106,7 +106,7 @@ public void Denormalize_With_Value_And_Precision_Returns_Expected_Denormalized_V public void TryReadValue_With_Position_At_Buffer_Length_Returns_False() { // Arrange ReadOnlyMemory buffer = "_p~iF~ps|U".AsMemory(); - int delta = 0; + long delta = 0; int position = buffer.Length; // Act @@ -114,7 +114,7 @@ public void TryReadValue_With_Position_At_Buffer_Length_Returns_False() { // Assert Assert.IsFalse(result); - Assert.AreEqual(0, delta); + Assert.AreEqual(0L, delta); } /// @@ -124,7 +124,7 @@ public void TryReadValue_With_Position_At_Buffer_Length_Returns_False() { public void TryReadValue_With_Position_Exceeds_Buffer_Length_Returns_False() { // Arrange ReadOnlyMemory buffer = "_p~iF~ps|U".AsMemory(); - int delta = 0; + long delta = 0; int position = buffer.Length + 1; // Act @@ -132,7 +132,7 @@ public void TryReadValue_With_Position_Exceeds_Buffer_Length_Returns_False() { // Assert Assert.IsFalse(result); - Assert.AreEqual(0, delta); + Assert.AreEqual(0L, delta); } /// @@ -143,7 +143,7 @@ public void TryReadValue_With_Positive_Single_Char_Reads_Value_And_Returns_True( // Arrange // Encode value 5: zigzag = 10 = 0x0A; char = 10 + 63 = 73 = 'I' ReadOnlyMemory buffer = "I".AsMemory(); // Single char encoding of value 5 - int delta = 0; + long delta = 0; int position = 0; // Act @@ -151,7 +151,7 @@ public void TryReadValue_With_Positive_Single_Char_Reads_Value_And_Returns_True( // Assert Assert.IsTrue(result); - Assert.AreEqual(5, delta); + Assert.AreEqual(5L, delta); Assert.AreEqual(1, position); } @@ -163,7 +163,7 @@ public void TryReadValue_With_Positive_Multi_Char_Reads_Value_And_Returns_True() // Arrange // _p~iF encodes latitude 38.5 (normalized = 3850000, zigzag = 7700000) ReadOnlyMemory buffer = "_p~iF".AsMemory(); - int delta = 0; + long delta = 0; int position = 0; // Act @@ -171,7 +171,7 @@ public void TryReadValue_With_Positive_Multi_Char_Reads_Value_And_Returns_True() // Assert Assert.IsTrue(result); - Assert.AreEqual(3850000, delta); + Assert.AreEqual(3850000L, delta); Assert.AreEqual(5, position); } @@ -183,7 +183,7 @@ public void TryReadValue_With_Negative_Value_Reads_Value_And_Returns_True() { // Arrange // ~ps|U encodes longitude -120.2 (normalized = -12020000, zigzag encodes negative) ReadOnlyMemory buffer = "~ps|U".AsMemory(); - int delta = 0; + long delta = 0; int position = 0; // Act @@ -191,7 +191,7 @@ public void TryReadValue_With_Negative_Value_Reads_Value_And_Returns_True() { // Assert Assert.IsTrue(result); - Assert.AreEqual(-12020000, delta); + Assert.AreEqual(-12020000L, delta); Assert.AreEqual(5, position); } @@ -202,7 +202,7 @@ public void TryReadValue_With_Negative_Value_Reads_Value_And_Returns_True() { public void TryReadValue_With_Existing_Delta_Accumulates_Delta() { // Arrange ReadOnlyMemory buffer = "I".AsMemory(); // encodes 5 - int delta = 10; // existing delta + long delta = 10; // existing delta int position = 0; // Act @@ -210,7 +210,7 @@ public void TryReadValue_With_Existing_Delta_Accumulates_Delta() { // Assert Assert.IsTrue(result); - Assert.AreEqual(15, delta); // 10 + 5 = 15 + Assert.AreEqual(15L, delta); // 10 + 5 = 15 } /// @@ -220,12 +220,12 @@ public void TryReadValue_With_Existing_Delta_Accumulates_Delta() { public void TryReadValue_With_Multiple_Values_Reads_Sequentially() { // Arrange - "_p~iF~ps|U" encodes lat 38.5 then delta lon -120.2 ReadOnlyMemory buffer = "_p~iF~ps|U".AsMemory(); - int delta = 0; + long delta = 0; int position = 0; // Act - read first value bool first = PolylineEncoding.TryReadValue(ref delta, buffer, ref position); - int firstDelta = delta; + long firstDelta = delta; // read second value bool second = PolylineEncoding.TryReadValue(ref delta, buffer, ref position); @@ -233,7 +233,7 @@ public void TryReadValue_With_Multiple_Values_Reads_Sequentially() { // Assert Assert.IsTrue(first); Assert.IsTrue(second); - Assert.AreEqual(3850000, firstDelta); + Assert.AreEqual(3850000L, firstDelta); Assert.AreEqual(10, position); // consumed all 10 chars } @@ -244,7 +244,7 @@ public void TryReadValue_With_Multiple_Values_Reads_Sequentially() { public void TryReadValue_With_Buffer_Ends_Mid_Value_Returns_False() { // Arrange - truncate a multi-char encoding ReadOnlyMemory buffer = "_p~".AsMemory(); // incomplete multi-char encoding - int delta = 0; + long delta = 0; int position = 0; // Act @@ -261,7 +261,7 @@ public void TryReadValue_With_Buffer_Ends_Mid_Value_Returns_False() { public void TryReadValue_Starting_From_Middle_Reads_Correctly() { // Arrange - "_p~iF~ps|U": start at position 5 to read the longitude value ReadOnlyMemory buffer = "_p~iF~ps|U".AsMemory(); - int delta = 0; + long delta = 0; int position = 5; // Act @@ -269,7 +269,7 @@ public void TryReadValue_Starting_From_Middle_Reads_Correctly() { // Assert Assert.IsTrue(result); - Assert.AreEqual(-12020000, delta); + Assert.AreEqual(-12020000L, delta); Assert.AreEqual(10, position); } @@ -491,7 +491,7 @@ public void TryWriteValue_With_Large_Negative_Value_Writes_Correctly() { [DataRow(16, 2)] [DataRow(3778903, 5)] [DataRow(-12241230, 5)] - public void GetRequiredBufferSize_Returns_Expected_Size(int delta, int expectedSize) { + public void GetRequiredBufferSize_Returns_Expected_Size(long delta, int expectedSize) { // Act int size = PolylineEncoding.GetRequiredBufferSize(delta); @@ -505,24 +505,24 @@ public void GetRequiredBufferSize_Returns_Expected_Size(int delta, int expectedS [TestMethod] public void GetRequiredBufferSize_With_Max_Int_Returns_Correct_Size() { // Act - int size = PolylineEncoding.GetRequiredBufferSize(int.MaxValue); + int size = PolylineEncoding.GetRequiredBufferSize(long.MaxValue); // Assert Assert.IsGreaterThan(0, size); - Assert.IsLessThanOrEqualTo(7, size); // Maximum size for int32 + Assert.IsLessThanOrEqualTo(13, size); // Maximum size for int64 } /// - /// Tests that GetRequiredBufferSize returns a valid size for the minimum negative integer. + /// Tests that GetRequiredBufferSize returns a valid size for the minimum negative long. /// [TestMethod] public void GetRequiredBufferSize_With_Min_Int_Returns_Correct_Size() { // Act - int size = PolylineEncoding.GetRequiredBufferSize(int.MinValue); + int size = PolylineEncoding.GetRequiredBufferSize(long.MinValue); // Assert Assert.IsGreaterThan(0, size); - Assert.IsLessThanOrEqualTo(7, size); // Maximum size for int32 + Assert.IsLessThanOrEqualTo(13, size); // Maximum size for int64 } /// @@ -531,7 +531,7 @@ public void GetRequiredBufferSize_With_Min_Int_Returns_Correct_Size() { [TestMethod] public void GetRequiredBufferSize_Consistent_With_TryWriteValue_Matches_Actual_Size() { // Arrange - const int delta = 3778903; + const long delta = 3778903L; int expectedSize = PolylineEncoding.GetRequiredBufferSize(delta); Span buffer = stackalloc char[expectedSize]; int position = 0; @@ -550,7 +550,7 @@ public void GetRequiredBufferSize_Consistent_With_TryWriteValue_Matches_Actual_S [TestMethod] public void GetRequiredBufferSize_With_Undersized_Buffer_Causes_TryWriteValue_To_Fail() { // Arrange - const int delta = 3778903; + const long delta = 3778903L; int requiredSize = PolylineEncoding.GetRequiredBufferSize(delta); Span buffer = stackalloc char[requiredSize - 1]; // One char too small int position = 0; @@ -609,7 +609,7 @@ public void ValidateFormat_With_Single_Terminator_Does_Not_Throw() { [TestMethod] public void ValidateFormat_With_Block_Too_Long_Throws_InvalidPolylineException() { // Act & Assert - Assert.ThrowsExactly(() => PolylineEncoding.ValidateFormat("________?")); // 8-char block (max is 7) + Assert.ThrowsExactly(() => PolylineEncoding.ValidateFormat("_____________?")); // 14-char block (max is 13) } #endregion @@ -655,7 +655,8 @@ public void ValidateCharRange_With_Invalid_Character_Throws_InvalidPolylineExcep [TestMethod] [DataRow("?")] // single terminator [DataRow("_p~iF~ps|U")] // multiple blocks - [DataRow("______?")] // 6 continuation chars + terminator (maximum block length) + [DataRow("______?")] // 6 continuation chars + terminator (block length 7) + [DataRow("____________?")] // 12 continuation chars + terminator (maximum block length 13) [DataRow("??")] // consecutive terminators [DataRow("?__?_____?")] // mixed block lengths public void ValidateBlockLength_With_Valid_Polyline_Does_Not_Throw(string polyline) { @@ -667,11 +668,11 @@ public void ValidateBlockLength_With_Valid_Polyline_Does_Not_Throw(string polyli /// Tests that ValidateBlockLength throws for invalid block structures. /// [TestMethod] - [DataRow("________?")] // 8-char block (exceeds max of 7) + [DataRow("_____________?")] // 13-char block (exceeds max of 13 = blockLen 14) [DataRow("________")] // all continuation chars, no terminator [DataRow("")] // empty polyline has no terminator - [DataRow("?________?")] // valid block then too-long block - [DataRow("________?_?")] // first block too long + [DataRow("?_____________?")] // valid block then too-long block + [DataRow("_____________?_?")] // first block too long public void ValidateBlockLength_With_Invalid_Polyline_Throws_InvalidPolylineException(string polyline) { // Act & Assert Assert.ThrowsExactly(() => PolylineEncoding.ValidateBlockLength(polyline)); From c1f5efd4684662c2669f38a8eeaff94952a4cde9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:46:11 +0000 Subject: [PATCH 14/22] fix confusing block-length test comments Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/b16b1bac-62fc-44de-9c08-88c566cf476e Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs b/tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs index 72748b0a..04063082 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineEncodingTests.cs @@ -609,7 +609,7 @@ public void ValidateFormat_With_Single_Terminator_Does_Not_Throw() { [TestMethod] public void ValidateFormat_With_Block_Too_Long_Throws_InvalidPolylineException() { // Act & Assert - Assert.ThrowsExactly(() => PolylineEncoding.ValidateFormat("_____________?")); // 14-char block (max is 13) + Assert.ThrowsExactly(() => PolylineEncoding.ValidateFormat("_____________?")); // 13 continuation chars + terminator = block length 14, exceeds max of 13 } #endregion @@ -668,7 +668,7 @@ public void ValidateBlockLength_With_Valid_Polyline_Does_Not_Throw(string polyli /// Tests that ValidateBlockLength throws for invalid block structures. /// [TestMethod] - [DataRow("_____________?")] // 13-char block (exceeds max of 13 = blockLen 14) + [DataRow("_____________?")] // 13 continuation chars + terminator = block length 14, exceeds max of 13 [DataRow("________")] // all continuation chars, no terminator [DataRow("")] // empty polyline has no terminator [DataRow("?_____________?")] // valid block then too-long block From 56f573364df2188ec1de22afdf503d0c75f872c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:27:59 +0000 Subject: [PATCH 15/22] docs: remove misleading 'epoch' terminology from SetBaseline/GetBaseline XML comments Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/14462e5e-f3ab-4b1a-aabf-b61049ff1191 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../Abstraction/IPolylineFormatter.cs | 5 +++-- src/PolylineAlgorithm/FormatterBuilder.cs | 16 +++++++++++----- src/PolylineAlgorithm/Internal/FormatterRule.cs | 5 +++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs index 68dc2bb3..ad31460c 100644 --- a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs +++ b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs @@ -29,8 +29,9 @@ public interface IPolylineFormatter { int Width { get; } /// - /// Returns the baseline (epoch) for the column at , or 0 if none is configured. - /// The encoder subtracts this value from the first item's scaled column value to keep the initial delta small. + /// Returns the baseline for the column at , or 0 if none is configured. + /// The encoder uses this as the starting point for the first item's delta computation: the initial + /// delta for the column is scaled_first_value − baseline rather than scaled_first_value. /// /// The zero-based column index. Must be in the range [0, ). /// The baseline value, or 0 when no baseline has been defined for the column. diff --git a/src/PolylineAlgorithm/FormatterBuilder.cs b/src/PolylineAlgorithm/FormatterBuilder.cs index ae27133f..1189ee7f 100644 --- a/src/PolylineAlgorithm/FormatterBuilder.cs +++ b/src/PolylineAlgorithm/FormatterBuilder.cs @@ -17,7 +17,7 @@ namespace PolylineAlgorithm; /// /// /// Use to obtain an instance, call once per column, -/// optionally chain to specify an epoch for the most-recently added column, +/// optionally chain to set a reference baseline for the most-recently added column, /// optionally chain to register a factory for the decoding direction, /// call to supply the polyline surface delegates (required), then call /// to produce the immutable . @@ -87,11 +87,17 @@ public FormatterBuilder AddValue(string name, Func - /// Sets a baseline (epoch) on the most-recently added column. - /// During encoding the baseline is subtracted from the first item's scaled column value, - /// keeping the initial delta small when the absolute first value is large. + /// Sets a reference value (baseline) on the most-recently added column. + /// During encoding, the baseline is subtracted from the first item's scaled column value so that + /// the initial delta is scaled_first_value − baseline rather than scaled_first_value. + /// Use this when the absolute scaled value of the first data point for a column would otherwise + /// produce a very large initial encoded delta. /// - /// The baseline value to apply to the first item's column value. + /// + /// The reference value to subtract from the first item's scaled column value during encoding. + /// The decoder automatically adds this value back, so the reconstructed item matches the + /// original input. + /// /// The current builder instance for method chaining. /// /// Thrown when no rules have been added yet. Call before diff --git a/src/PolylineAlgorithm/Internal/FormatterRule.cs b/src/PolylineAlgorithm/Internal/FormatterRule.cs index 5914255f..04ae1f1b 100644 --- a/src/PolylineAlgorithm/Internal/FormatterRule.cs +++ b/src/PolylineAlgorithm/Internal/FormatterRule.cs @@ -40,8 +40,9 @@ internal FormatterRule(string name, long factor, Func select, long? b internal long Factor { get; } /// - /// Gets the optional baseline (epoch). When set, the encoder subtracts this value from the - /// first point's scaled column value to keep the initial delta small. + /// Gets the optional reference value used as the baseline for the first encoded point. + /// When set, the encoder subtracts this value from the first item's scaled column value so that + /// the initial delta is scaled_first_value − baseline rather than scaled_first_value. /// internal long? Baseline { get; } From bf07a29aeb984f35a1f253db5e3bfce5c9f2dd39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:02:04 +0000 Subject: [PATCH 16/22] docs: update README to use new FormatterBuilder/PolylineFormatter API Replace all references to AbstractPolylineEncoder, AbstractPolylineDecoder, PolylineEncodingOptions, and PolylineEncodingOptionsBuilder with the new fluent FormatterBuilder API. - Features list updated to mention FormatterBuilder, PolylineFormatter, PolylineEncoder, PolylineDecoder, and PolylineOptions - Usage section rewritten with Quick Start, Building a formatter, Encoding, Decoding, and Advanced options subsections - All code examples replaced with new API patterns - FAQ updated to reference FormatterBuilder and PolylineOptions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- README.md | 108 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 0555c16f..5c6802f3 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,12 @@ Google's Encoded Polyline Algorithm compresses sequences of geographic coordinat ## Features - Fully compliant Google Encoded Polyline Algorithm for .NET Standard 2.1+ -- Extensible APIs — implement your own encoder/decoder for any coordinate or polyline type -- Robust input validation with descriptive exceptions for malformed or out-of-range data -- Advanced configuration via `PolylineEncodingOptions` (precision, buffer size, logging) +- Fully fluent `FormatterBuilder` — configure coordinate fields, factories, and polyline I/O in one chain +- Sealed, immutable `PolylineFormatter` produced by the builder +- Type-safe `PolylineEncoder` and `PolylineDecoder` with no inheritance required +- `PolylineOptions` for stack-alloc limits and optional logging - Extension methods for encoding directly from `List` and arrays +- Robust input validation with descriptive exceptions for malformed or out-of-range data - Logging and diagnostic support via `Microsoft.Extensions.Logging` - Low-level utilities for normalization, validation, and bit-level operations via static `PolylineEncoding` class - Thread-safe, stateless APIs @@ -48,75 +50,101 @@ Install-Package PolylineAlgorithm ## Usage -The library provides abstract base classes to implement your own encoder and decoder for any coordinate and polyline type. Inherit from `AbstractPolylineEncoder` or `AbstractPolylineDecoder`, override the coordinate accessors, then call `Encode` or `Decode`. +The library uses a fluent `FormatterBuilder` to describe how to map between your coordinate type and a polyline type — no inheritance required. Build a `PolylineFormatter`, wrap it in `PolylineOptions`, then instantiate `PolylineEncoder` and `PolylineDecoder`. ### Quick Start ```csharp -// 1. Implement a minimal encoder (see full example below) -var encoder = new MyPolylineEncoder(); -string encoded = encoder.Encode(coordinates); // e.g. "yseiHoc_MwacOjnwM" +using PolylineAlgorithm; + +// 1. Build a formatter that maps (double Lat, double Lon) ↔ string polyline +PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", static c => c.Lat) + .AddValue("lon", static c => c.Lon) + .WithCreate(static v => (v[0], v[1])) + .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .Build(); + +PolylineOptions<(double Lat, double Lon), string> options = new(formatter); + +PolylineEncoder<(double Lat, double Lon), string> encoder = new(options); +PolylineDecoder decoder = new(options); + +// 2. Encode +var coordinates = new List<(double, double)> { (48.858370, 2.294481), (51.500729, -0.124625) }; +string encoded = encoder.Encode(coordinates); // extension method for List +// Output: "yseiHoc_MwacOjnwM" -// 2. Implement a minimal decoder (see full example below) -var decoder = new MyPolylineDecoder(); -IEnumerable<(double Latitude, double Longitude)> decoded = decoder.Decode(encoded); +// 3. Decode +IEnumerable<(double Lat, double Lon)> decoded = decoder.Decode(encoded); ``` -### Custom encoder and decoder +### Building a formatter -#### Encoding +`FormatterBuilder` configures how the library reads and writes your types: -Custom encoder implementation. +| Method | Purpose | +|---|---| +| `FormatterBuilder.Create()` | Static factory to start building | +| `.AddValue(name, selector, precision=5)` | Register a coordinate field (latitude, longitude, …) | +| `.SetBaseline(long)` | Override the encoding baseline (optional) | +| `.WithCreate(factory)` | Factory delegate `PolylineItemFactory`: `TC(ReadOnlySpan values)` — required for decoding | +| `.ForPolyline(write, read)` | How to convert `ReadOnlyMemory` → `TP` and `TP` → `ReadOnlyMemory` | +| `.Build()` | Returns an immutable `PolylineFormatter` | ```csharp -using PolylineAlgorithm; -using PolylineAlgorithm.Abstraction; - -public sealed class MyPolylineEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string> { - protected override double GetLatitude((double Latitude, double Longitude) coordinate) => coordinate.Latitude; - protected override double GetLongitude((double Latitude, double Longitude) coordinate) => coordinate.Longitude; - protected override string CreatePolyline(ReadOnlyMemory polyline) => polyline.ToString(); -} +PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", static c => c.Lat) + .AddValue("lon", static c => c.Lon) + .WithCreate(static v => (v[0], v[1])) + .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .Build(); ``` -Custom encoder usage. +### Encoding ```csharp +using PolylineAlgorithm; using PolylineAlgorithm.Extensions; -var coordinates = new List<(double Latitude, double Longitude)> +PolylineOptions<(double Lat, double Lon), string> options = new(formatter); +PolylineEncoder<(double Lat, double Lon), string> encoder = new(options); + +var coordinates = new List<(double Lat, double Lon)> { (48.858370, 2.294481), (51.500729, -0.124625) }; -var encoder = new MyPolylineEncoder(); string encoded = encoder.Encode(coordinates); // extension method for List - -Console.WriteLine(encoded); +Console.WriteLine(encoded); // yseiHoc_MwacOjnwM ``` -#### Decoding - -Custom decoder implementation. +### Decoding ```csharp using PolylineAlgorithm; -using PolylineAlgorithm.Abstraction; -public sealed class MyPolylineDecoder : AbstractPolylineDecoder { - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) => (latitude, longitude); - protected override ReadOnlyMemory GetReadOnlyMemory(in string polyline) => polyline.AsMemory(); -} +PolylineOptions<(double Lat, double Lon), string> options = new(formatter); +PolylineDecoder decoder = new(options); + +IEnumerable<(double Lat, double Lon)> decoded = decoder.Decode("yseiHoc_MwacOjnwM"); ``` -Custom decoder usage. +### Advanced options (logging, stack-alloc limit) ```csharp -string encoded = "yseiHoc_MwacOjnwM"; +using Microsoft.Extensions.Logging; + +PolylineOptions<(double Lat, double Lon), string> options = new( + formatter, + stackAllocLimit: 1024, + loggerFactory: loggerFactory); -var decoder = new MyPolylineDecoder(); -IEnumerable<(double Latitude, double Longitude)> decoded = decoder.Decode(encoded); +var encoder = new PolylineEncoder<(double Lat, double Lon), string>(options); +var decoder = new PolylineDecoder(options); ``` > **Note:** @@ -148,7 +176,7 @@ A: All platforms supporting `netstandard2.1` (including .NET Core and .NET 5+). A: The decoder will throw descriptive exceptions (`InvalidPolylineException`) for malformed polyline strings. Check exception handling in your application. **Q: How do I customize encoding options (e.g., precision, buffer size, logging)?** -A: Use `PolylineEncodingOptionsBuilder` to set custom options and pass the built `PolylineEncodingOptions` to the encoder or decoder constructor. +A: Pass a `PolylineOptions` to the encoder/decoder constructor. Set `stackAllocLimit` to control buffer size and `loggerFactory` for logging. Precision is set per-field via `.AddValue(name, selector, precision)` on the `FormatterBuilder`. **Q: Is the library thread-safe?** A: Yes, the main encoding and decoding APIs are stateless and thread-safe. If using mutable shared resources, manage synchronization in your code. @@ -160,7 +188,7 @@ A: Yes! Any environment supporting `netstandard2.1` can use this library. A: Open a GitHub issue using the provided templates in the repository and tag @petesramek. **Q: Is there support for elevation, time stamps, or third coordinate values?** -A: Not currently, not planned to be added, but you can extend by implementing your own encoder/decoder using `PolylineEncoding` class methods. +A: Not currently, not planned to be added, but you can extend by adding extra `.AddValue(...)` calls in your `FormatterBuilder` and using `PolylineEncoding` class methods for low-level operations. **Q: How do I contribute documentation improvements?** A: Update XML doc comments in the codebase and submit a PR; all public APIs require XML documentation. To improve guides, update the relevant markdown file in the `/api-reference/guide` folder. From 7b34b08ce65e798c2acf4e7fec35b6bd317e04dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:15:46 +0000 Subject: [PATCH 17/22] feat: update PublicAPI.Unshipped.txt and rewrite both READMEs for new FormatterBuilder API Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/34578db3-a4b3-4d4e-b444-f9cc1f46bbc5 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 57 ++++++++++++++++ src/PolylineAlgorithm/README.md | 65 ++++++++----------- 2 files changed, 85 insertions(+), 37 deletions(-) diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index 7dc5c581..d49ab754 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -1 +1,58 @@ #nullable enable +PolylineAlgorithm.Abstraction.IPolylineDecoder +PolylineAlgorithm.Abstraction.IPolylineDecoder.Decode(TPolyline polyline, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable! +PolylineAlgorithm.Abstraction.IPolylineEncoder +PolylineAlgorithm.Abstraction.IPolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline +PolylineAlgorithm.Abstraction.IPolylineFormatter +PolylineAlgorithm.Abstraction.IPolylineFormatter.CreateItem(System.ReadOnlySpan values) -> TCoordinate +PolylineAlgorithm.Abstraction.IPolylineFormatter.GetBaseline(int index) -> long +PolylineAlgorithm.Abstraction.IPolylineFormatter.GetValues(TCoordinate item, System.Span values) -> void +PolylineAlgorithm.Abstraction.IPolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory +PolylineAlgorithm.Abstraction.IPolylineFormatter.Width.get -> int +PolylineAlgorithm.Abstraction.IPolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline +PolylineAlgorithm.Extensions.PolylineDecoderExtensions +PolylineAlgorithm.Extensions.PolylineEncoderExtensions +PolylineAlgorithm.FormatterBuilder +PolylineAlgorithm.FormatterBuilder.AddValue(string! name, System.Func! selector, uint precision = 5) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.FormatterBuilder.Build() -> PolylineAlgorithm.PolylineFormatter! +PolylineAlgorithm.FormatterBuilder.ForPolyline(System.Func, TPolyline>! write, System.Func>! read) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.FormatterBuilder.SetBaseline(long baseline) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.FormatterBuilder.WithCreate(PolylineAlgorithm.PolylineItemFactory! create) -> PolylineAlgorithm.FormatterBuilder! +PolylineAlgorithm.InvalidPolylineException +PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException() -> void +PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException(string! message, System.Exception! innerException) -> void +PolylineAlgorithm.PolylineDecoder +PolylineAlgorithm.PolylineDecoder.Decode(TPolyline polyline, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable! +PolylineAlgorithm.PolylineDecoder.PolylineDecoder(PolylineAlgorithm.PolylineOptions! options) -> void +PolylineAlgorithm.PolylineEncoder +PolylineAlgorithm.PolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline +PolylineAlgorithm.PolylineEncoder.PolylineEncoder(PolylineAlgorithm.PolylineOptions! options) -> void +PolylineAlgorithm.PolylineEncoding +PolylineAlgorithm.PolylineFormatter +PolylineAlgorithm.PolylineFormatter.CreateItem(System.ReadOnlySpan values) -> TCoordinate +PolylineAlgorithm.PolylineFormatter.GetBaseline(int index) -> long +PolylineAlgorithm.PolylineFormatter.GetValues(TCoordinate item, System.Span values) -> void +PolylineAlgorithm.PolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory +PolylineAlgorithm.PolylineFormatter.Width.get -> int +PolylineAlgorithm.PolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline +PolylineAlgorithm.PolylineItemFactory +PolylineAlgorithm.PolylineOptions +PolylineAlgorithm.PolylineOptions.Formatter.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! +PolylineAlgorithm.PolylineOptions.LoggerFactory.get -> Microsoft.Extensions.Logging.ILoggerFactory! +PolylineAlgorithm.PolylineOptions.PolylineOptions(PolylineAlgorithm.Abstraction.IPolylineFormatter! formatter, int stackAllocLimit = 512, Microsoft.Extensions.Logging.ILoggerFactory? loggerFactory = null) -> void +PolylineAlgorithm.PolylineOptions.StackAllocLimit.get -> int +static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder, TValue>! decoder, string! polyline) -> System.Collections.Generic.IEnumerable! +static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder! decoder, System.ReadOnlyMemory polyline) -> System.Collections.Generic.IEnumerable! +static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder! decoder, char[]! polyline) -> System.Collections.Generic.IEnumerable! +static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IPolylineEncoder! encoder, System.Collections.Generic.List! coordinates) -> TPolyline +static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IPolylineEncoder! encoder, TCoordinate[]! coordinates) -> TPolyline +static PolylineAlgorithm.FormatterBuilder.Create() -> PolylineAlgorithm.FormatterBuilder! +static PolylineAlgorithm.PolylineEncoding.Denormalize(long value, uint precision = 5) -> double +static PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(long delta) -> int +static PolylineAlgorithm.PolylineEncoding.Normalize(double value, uint precision = 5) -> long +static PolylineAlgorithm.PolylineEncoding.TryReadValue(ref long delta, System.ReadOnlyMemory buffer, ref int position) -> bool +static PolylineAlgorithm.PolylineEncoding.TryWriteValue(long delta, System.Span buffer, ref int position) -> bool +static PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan polyline) -> void +static PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan polyline) -> void +static PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan polyline) -> void +virtual PolylineAlgorithm.PolylineItemFactory.Invoke(System.ReadOnlySpan values) -> T diff --git a/src/PolylineAlgorithm/README.md b/src/PolylineAlgorithm/README.md index 97c4bb98..3a8e98dd 100644 --- a/src/PolylineAlgorithm/README.md +++ b/src/PolylineAlgorithm/README.md @@ -7,10 +7,12 @@ Google's Encoded Polyline Algorithm compresses sequences of geographic coordinat ## Features - Google-compliant polyline encoding/decoding for geographic coordinates -- Extensible APIs for custom coordinate and polyline types (`AbstractPolylineEncoder`, `AbstractPolylineDecoder`) +- Fluent `FormatterBuilder` for configuring custom coordinate and polyline types +- Immutable, sealed `PolylineFormatter` built via `FormatterBuilder` +- `PolylineEncoder` and `PolylineDecoder` — configurable via `PolylineOptions` - Extension methods for encoding from `List` and arrays (`PolylineEncoderExtensions`) - Robust input validation and descriptive exceptions -- Configurable with `PolylineEncodingOptions` (precision, buffer size, logging) +- Configurable stack-alloc buffer size and logging via `PolylineOptions` - Thread-safe, stateless APIs - Low-level utilities via static `PolylineEncoding` class (Normalize, Denormalize, TryReadValue, TryWriteValue, ValidateFormat, etc.) - Benchmarks and unit tests for correctness and performance @@ -31,19 +33,20 @@ Install-Package PolylineAlgorithm ## Quick Start -The library provides abstract base classes to build your own encoder and decoder for any coordinate and polyline type. +Use `FormatterBuilder` to configure your coordinate and polyline types, then construct `PolylineEncoder` and `PolylineDecoder` from the resulting `PolylineOptions`. -### Implement a custom encoder +### Build a formatter ```csharp using PolylineAlgorithm; -using PolylineAlgorithm.Abstraction; -public sealed class MyPolylineEncoder : AbstractPolylineEncoder<(double Latitude, double Longitude), string> { - protected override double GetLatitude((double Latitude, double Longitude) coordinate) => coordinate.Latitude; - protected override double GetLongitude((double Latitude, double Longitude) coordinate) => coordinate.Longitude; - protected override string CreatePolyline(ReadOnlyMemory polyline) => polyline.ToString(); -} +PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", static c => c.Lat) + .AddValue("lon", static c => c.Lon) + .WithCreate(static v => (v[0], v[1])) + .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .Build(); ``` ### Encode coordinates @@ -51,55 +54,43 @@ public sealed class MyPolylineEncoder : AbstractPolylineEncoder<(double Latitude ```csharp using PolylineAlgorithm.Extensions; -var coordinates = new List<(double Latitude, double Longitude)> +PolylineOptions<(double Lat, double Lon), string> options = new(formatter); +PolylineEncoder<(double Lat, double Lon), string> encoder = new(options); + +var coordinates = new List<(double Lat, double Lon)> { (48.858370, 2.294481), (51.500729, -0.124625) }; -var encoder = new MyPolylineEncoder(); string encoded = encoder.Encode(coordinates); // extension method for List Console.WriteLine(encoded); // Output: "yseiHoc_MwacOjnwM" ``` -### Implement a custom decoder - -```csharp -using PolylineAlgorithm; -using PolylineAlgorithm.Abstraction; - -public sealed class MyPolylineDecoder : AbstractPolylineDecoder { - protected override (double Latitude, double Longitude) CreateCoordinate(double latitude, double longitude) => (latitude, longitude); - protected override ReadOnlyMemory GetReadOnlyMemory(in string polyline) => polyline.AsMemory(); -} -``` - ### Decode polyline ```csharp -string encoded = "yseiHoc_MwacOjnwM"; +PolylineDecoder decoder = new(options); -var decoder = new MyPolylineDecoder(); -IEnumerable<(double Latitude, double Longitude)> decoded = decoder.Decode(encoded); +IEnumerable<(double Lat, double Lon)> decoded = decoder.Decode("yseiHoc_MwacOjnwM"); ``` ## Advanced Usage -Use `PolylineEncodingOptionsBuilder` to customize precision, buffer size, and logging, then pass the built options to the encoder or decoder constructor: +Pass a `stackAllocLimit` and an `ILoggerFactory` to `PolylineOptions` to customize buffer sizing and logging: ```csharp using Microsoft.Extensions.Logging; -PolylineEncodingOptions options = PolylineEncodingOptionsBuilder.Create() - .WithPrecision(6) // 6 decimal places instead of the default 5 - .WithStackAllocLimit(1024) // increase stack-alloc buffer - .WithLoggerFactory(loggerFactory) // plug in your ILoggerFactory - .Build(); +PolylineOptions<(double Lat, double Lon), string> options = new( + formatter, + stackAllocLimit: 1024, + loggerFactory: loggerFactory); -var encoder = new MyPolylineEncoder(options); -var decoder = new MyPolylineDecoder(options); +PolylineEncoder<(double Lat, double Lon), string> encoder = new(options); +PolylineDecoder decoder = new(options); ``` Use static methods on `PolylineEncoding` for low-level normalization, validation, and bit-level read/write operations. @@ -114,8 +105,8 @@ Use static methods on `PolylineEncoding` for low-level normalization, validation The decoder throws `InvalidPolylineException` with a descriptive message. Wrap calls in a try/catch in your application. - **What .NET versions are supported?** Any environment supporting `netstandard2.1` -- **How do I customize encoder options?** - Use `PolylineEncodingOptionsBuilder` and pass the built options to the encoder or decoder constructor. +- **How do I customize encoder/decoder options?** + Create a `PolylineOptions` with your `PolylineFormatter`, optional `stackAllocLimit`, and optional `ILoggerFactory`, then pass it to the `PolylineEncoder` or `PolylineDecoder` constructor. - **Where can I get help?** [GitHub issues](https://github.com/petesramek/polyline-algorithm-csharp/issues) From 446f8c33a08b6dd309d67affa66059bebbd898c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:19:02 +0000 Subject: [PATCH 18/22] style: use static lambdas in test helper setup for encoder/decoder Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/34578db3-a4b3-4d4e-b444-f9cc1f46bbc5 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../Abstraction/AbstractPolylineDecoderTests.cs | 4 ++-- .../Abstraction/AbstractPolylineEncoderTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs index e1a1d3f8..97d2edbf 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs @@ -25,8 +25,8 @@ public sealed class AbstractPolylineDecoderTests { private static PolylineDecoder CreateDecoder() { PolylineFormatter<(double Lat, double Lon), string> formatter = FormatterBuilder<(double Lat, double Lon), string>.Create() - .AddValue("lat", c => c.Lat) - .AddValue("lon", c => c.Lon) + .AddValue("lat", static c => c.Lat) + .AddValue("lon", static c => c.Lon) .WithCreate(static v => (v[0], v[1])) .ForPolyline(_write, _read) .Build(); diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs index a00d6417..62c38ea2 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs @@ -24,8 +24,8 @@ public sealed class AbstractPolylineEncoderTests { private static PolylineEncoder<(double Lat, double Lon), string> CreateEncoder(int stackAllocLimit = 512) { PolylineFormatter<(double Lat, double Lon), string> formatter = FormatterBuilder<(double Lat, double Lon), string>.Create() - .AddValue("lat", c => c.Lat) - .AddValue("lon", c => c.Lon) + .AddValue("lat", static c => c.Lat) + .AddValue("lon", static c => c.Lon) .ForPolyline(_write, _read) .Build(); From 7aa34a9d01a08083ce1e4d219fdbd9df425e0ae0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:56:42 +0000 Subject: [PATCH 19/22] feat: add PolylineEncodingOptions/PolylineDecodingOptions with IChunkedPolylineEncoder/Decoder for chunked encoding support Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/49b11574-fbb3-42e8-b1f2-8af7fc403268 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- .../Abstraction/IChunkedPolylineDecoder.cs | 52 ++++++ .../Abstraction/IChunkedPolylineEncoder.cs | 47 ++++++ .../Extensions/PolylineDecoderExtensions.cs | 34 ++++ .../Extensions/PolylineEncoderExtensions.cs | 74 +++++++++ src/PolylineAlgorithm/PolylineDecoder.cs | 96 ++++++++++- .../PolylineDecodingOptions.cs | 51 ++++++ src/PolylineAlgorithm/PolylineEncoder.cs | 106 ++++++++++++- .../PolylineEncodingOptions.cs | 52 ++++++ src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 19 +++ .../AbstractPolylineDecoderTests.cs | 150 ++++++++++++++++++ .../AbstractPolylineEncoderTests.cs | 132 +++++++++++++++ 11 files changed, 808 insertions(+), 5 deletions(-) create mode 100644 src/PolylineAlgorithm/Abstraction/IChunkedPolylineDecoder.cs create mode 100644 src/PolylineAlgorithm/Abstraction/IChunkedPolylineEncoder.cs create mode 100644 src/PolylineAlgorithm/PolylineDecodingOptions.cs create mode 100644 src/PolylineAlgorithm/PolylineEncodingOptions.cs diff --git a/src/PolylineAlgorithm/Abstraction/IChunkedPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/IChunkedPolylineDecoder.cs new file mode 100644 index 00000000..d69849a1 --- /dev/null +++ b/src/PolylineAlgorithm/Abstraction/IChunkedPolylineDecoder.cs @@ -0,0 +1,52 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm.Abstraction; + +using System.Collections.Generic; +using System.Threading; + +/// +/// Provides per-call options-based chunked (stateless-continuation) decoding without inheriting the +/// covariant . Implement both interfaces on the +/// concrete decoder class to support both the standard and chunked overloads. +/// +/// The encoded polyline type. +/// The coordinate type. +/// +/// Use this interface when you need to decode a polyline that was produced by chunked encoding. +/// Pass set to the last coordinate of the +/// preceding decoded chunk to seed the accumulated-delta state correctly. +/// +public interface IChunkedPolylineDecoder { + /// + /// Decodes an encoded into a sequence of coordinates, applying + /// the per-call to control the accumulated-delta seed. + /// + /// The encoded polyline to decode. Must not be . + /// + /// Per-call options that control the starting accumulated-delta seed. Pass + /// or an instance with + /// set to to start from zero (the default behaviour). + /// + /// A token that can be used to cancel the operation. + /// + /// An of representing the decoded + /// coordinates. + /// + /// + /// Thrown when is . + /// + /// + /// Thrown when the polyline format is invalid or malformed. + /// + /// + /// Thrown when is canceled. + /// + IEnumerable Decode( + TPolyline polyline, + PolylineDecodingOptions? options, + CancellationToken cancellationToken); +} diff --git a/src/PolylineAlgorithm/Abstraction/IChunkedPolylineEncoder.cs b/src/PolylineAlgorithm/Abstraction/IChunkedPolylineEncoder.cs new file mode 100644 index 00000000..a1ee0b44 --- /dev/null +++ b/src/PolylineAlgorithm/Abstraction/IChunkedPolylineEncoder.cs @@ -0,0 +1,47 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm.Abstraction; + +using System; +using System.Collections.Generic; +using System.Threading; + +/// +/// Extends with a per-call options overload that +/// supports chunked (stateless-continuation) encoding. +/// +/// The coordinate type. +/// The encoded polyline type. +/// +/// Use this interface when you need to encode a large coordinate sequence in independent chunks that +/// can be concatenated into a single valid polyline. Pass +/// set to the last coordinate of the +/// preceding chunk to seed the delta baseline correctly. +/// +public interface IChunkedPolylineEncoder : IPolylineEncoder { + /// + /// Encodes a sequence of geographic coordinates into an encoded polyline, applying the per-call + /// to control the delta baseline. + /// + /// The collection of coordinates to encode. + /// + /// Per-call options that control the starting delta baseline. Pass + /// or an instance with + /// set to to use the formatter's default baseline. + /// + /// A token that can be used to cancel the operation. + /// + /// An instance of representing the encoded coordinates. + /// + /// Thrown when is empty. + /// + /// Thrown when is canceled. + /// + TPolyline Encode( + ReadOnlySpan coordinates, + PolylineEncodingOptions? options, + CancellationToken cancellationToken); +} diff --git a/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs b/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs index 9ef76a35..8da3dc72 100644 --- a/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs +++ b/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs @@ -97,4 +97,38 @@ public static IEnumerable Decode(this IPolylineDecoder + /// Decodes an encoded polyline string into a sequence of geographic coordinates, applying per-call + /// to seed the accumulated-delta state. + /// + /// The coordinate type returned by the decoder. + /// The chunked decoder instance. + /// The encoded polyline string to decode. + /// + /// Per-call options that control the accumulated-delta seed. Pass to start + /// from zero (the default behaviour). + /// + /// + /// An of containing the decoded + /// coordinate pairs. + /// + /// + /// Thrown when or is . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] + public static IEnumerable Decode( + this IChunkedPolylineDecoder decoder, + string polyline, + PolylineDecodingOptions? options) { + if (decoder is null) { + ExceptionGuard.ThrowArgumentNull(nameof(decoder)); + } + + if (polyline is null) { + ExceptionGuard.ThrowArgumentNull(nameof(polyline)); + } + + return decoder.Decode(polyline, options, CancellationToken.None); + } } diff --git a/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs b/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs index 2502dbf4..15b15415 100644 --- a/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs +++ b/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs @@ -83,4 +83,78 @@ public static TPolyline Encode(this IPolylineEncoder + /// Encodes a of instances into an encoded + /// polyline, applying per-call to control the delta baseline. + /// + /// The type that represents a geographic coordinate to encode. + /// The type that represents the encoded polyline output. + /// The chunked encoder instance. + /// The list of coordinates to encode. + /// + /// Per-call options that control the starting delta baseline. Pass to use + /// the formatter's default baseline. + /// + /// + /// A representing the encoded polyline. + /// + /// + /// Thrown when or is . + /// + [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.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] + public static TPolyline Encode( + this IChunkedPolylineEncoder encoder, + List coordinates, + PolylineEncodingOptions? options) { + if (encoder is null) { + ExceptionGuard.ThrowArgumentNull(nameof(encoder)); + } + + if (coordinates is null) { + ExceptionGuard.ThrowArgumentNull(nameof(coordinates)); + } + +#if NET5_0_OR_GREATER + return encoder.Encode(CollectionsMarshal.AsSpan(coordinates), options, CancellationToken.None); +#else + return encoder.Encode([.. coordinates], options, CancellationToken.None); +#endif + } + + /// + /// Encodes an array of instances into an encoded polyline, + /// applying per-call to control the delta baseline. + /// + /// The type that represents a geographic coordinate to encode. + /// The type that represents the encoded polyline output. + /// The chunked encoder instance. + /// The array of coordinates to encode. + /// + /// Per-call options that control the starting delta baseline. Pass to use + /// the formatter's default baseline. + /// + /// + /// A representing the encoded polyline. + /// + /// + /// Thrown when or is . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] + public static TPolyline Encode( + this IChunkedPolylineEncoder encoder, + TCoordinate[] coordinates, + PolylineEncodingOptions? options) { + if (encoder is null) { + ExceptionGuard.ThrowArgumentNull(nameof(encoder)); + } + + if (coordinates is null) { + ExceptionGuard.ThrowArgumentNull(nameof(coordinates)); + } + + return encoder.Encode(coordinates.AsSpan(), options, CancellationToken.None); + } } diff --git a/src/PolylineAlgorithm/PolylineDecoder.cs b/src/PolylineAlgorithm/PolylineDecoder.cs index 46e0c190..5303ffaa 100644 --- a/src/PolylineAlgorithm/PolylineDecoder.cs +++ b/src/PolylineAlgorithm/PolylineDecoder.cs @@ -24,7 +24,7 @@ namespace PolylineAlgorithm; /// to the constructor. The formatter handles /// all type-specific concerns; no subclassing is required. /// -public class PolylineDecoder : IPolylineDecoder { +public class PolylineDecoder : IPolylineDecoder, IChunkedPolylineDecoder { private readonly IPolylineFormatter _formatter; private readonly ILogger> _logger; @@ -114,4 +114,98 @@ public IEnumerable Decode(TPolyline polyline, CancellationToken can _logger.LogOperationFinishedDebug(OperationName); } } + + /// + /// Decodes an encoded into a sequence of + /// instances, applying per-call to + /// seed the accumulated-delta state. Use this overload to decode polylines that were produced by + /// chunked encoding. + /// + /// The encoded polyline to decode. Must not be . + /// + /// Per-call options that control the accumulated-delta seed. Pass or an + /// instance with set to + /// to start from zero (same as calling + /// ). + /// + /// A token that can be used to cancel the operation. + /// + /// An of representing the decoded + /// coordinates. + /// + /// + /// Thrown when is . + /// + /// + /// Thrown when the polyline format is invalid or malformed. + /// + /// + /// Thrown when is canceled during decoding. + /// + public IEnumerable Decode( + TPolyline polyline, + PolylineDecodingOptions? options, + CancellationToken cancellationToken) { + const string OperationName = nameof(Decode); + + _logger.LogOperationStartedDebug(OperationName); + + if (polyline is null) { + _logger.LogNullArgumentWarning(nameof(polyline)); + ExceptionGuard.ThrowArgumentNull(nameof(polyline)); + } + + ReadOnlyMemory sequence = _formatter.Read(polyline); + + if (sequence.Length < Defaults.Polyline.Block.Length.Min) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogPolylineCannotBeShorterThanWarning(sequence.Length, Defaults.Polyline.Block.Length.Min); + ExceptionGuard.ThrowInvalidPolylineLength(sequence.Length, Defaults.Polyline.Block.Length.Min); + } + + try { + PolylineEncoding.ValidateFormat(sequence.Span); + } catch (ArgumentException ex) { + _logger.LogInvalidPolylineFormatWarning(ex); + throw; + } + + int width = _formatter.Width; + long[] accumulated = new long[width]; + int position = 0; + + SeedAccumulated(accumulated, options); + + try { + while (position < sequence.Length) { + cancellationToken.ThrowIfCancellationRequested(); + + for (int j = 0; j < width; j++) { + if (!PolylineEncoding.TryReadValue(ref accumulated[j], sequence, ref position)) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogInvalidPolylineWarning(position); + ExceptionGuard.ThrowInvalidPolylineFormat(position); + } + } + + yield return _formatter.CreateItem(accumulated.AsSpan()); + } + } finally { + _logger.LogOperationFinishedDebug(OperationName); + } + } + + private void SeedAccumulated(long[] accumulated, PolylineDecodingOptions? options) { + if (options is not { HasPrevious: true }) { + return; + } + + int width = _formatter.Width; + long[] scaled = new long[width]; + _formatter.GetValues(options.Previous, scaled.AsSpan()); + + for (int j = 0; j < width; j++) { + accumulated[j] = scaled[j] - _formatter.GetBaseline(j); + } + } } diff --git a/src/PolylineAlgorithm/PolylineDecodingOptions.cs b/src/PolylineAlgorithm/PolylineDecodingOptions.cs new file mode 100644 index 00000000..facf782d --- /dev/null +++ b/src/PolylineAlgorithm/PolylineDecodingOptions.cs @@ -0,0 +1,51 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +/// +/// Per-call options for a chunked decoding operation. +/// +/// The coordinate type understood by the formatter. +/// +/// Pass an instance of this class to the chunked +/// overload to control +/// the accumulated-delta seed used at the start of each chunk. When is +/// zero-initialisation is used, which is the existing default behaviour. +/// +public sealed class PolylineDecodingOptions { + private readonly TCoordinate _previous; + + /// + /// Initializes a new instance of with no + /// previous coordinate (zero-initialised baseline will be used). + /// + public PolylineDecodingOptions() { } + + /// + /// Initializes a new instance of with the + /// specified previous coordinate used to seed the accumulated-delta state. + /// + /// + /// The last coordinate of the previous chunk, used to seed the accumulated-delta state. + /// + public PolylineDecodingOptions(TCoordinate previous) { + _previous = previous; + HasPrevious = true; + } + + /// + /// Gets a value indicating whether a previous coordinate has been supplied to seed the + /// accumulated-delta state. When zero-initialisation is used, which is + /// the existing default. + /// + public bool HasPrevious { get; } + + /// + /// Gets the last coordinate of the previous chunk, used to seed the accumulated-delta state. + /// Only meaningful when is . + /// + public TCoordinate Previous => _previous; +} diff --git a/src/PolylineAlgorithm/PolylineEncoder.cs b/src/PolylineAlgorithm/PolylineEncoder.cs index 0fccb6ae..4cc83944 100644 --- a/src/PolylineAlgorithm/PolylineEncoder.cs +++ b/src/PolylineAlgorithm/PolylineEncoder.cs @@ -25,7 +25,7 @@ namespace PolylineAlgorithm; /// to the constructor. The formatter handles /// all type-specific concerns; no subclassing is required. /// -public class PolylineEncoder : IPolylineEncoder { +public class PolylineEncoder : IChunkedPolylineEncoder { private readonly IPolylineFormatter _formatter; private readonly PolylineOptions _options; private readonly ILogger> _logger; @@ -95,9 +95,7 @@ public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken long[] previous = new long[width]; long[] values = new long[width]; - for (int j = 0; j < width; j++) { - previous[j] = _formatter.GetBaseline(j); - } + SeedPrevious(previous, null); try { for (int i = 0; i < coordinates.Length; i++) { @@ -131,6 +129,106 @@ public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken } } + /// + /// Encodes a collection of instances into an encoded + /// , applying per-call to control the + /// delta baseline. Use this overload to encode large sequences in independent chunks that can be + /// concatenated into a single valid polyline. + /// + /// The collection of coordinates to encode. + /// + /// Per-call options that control the starting delta baseline. Pass or an + /// instance with set to + /// to use the formatter's default baseline (same as calling + /// ). + /// + /// A token that can be used to cancel the operation. + /// + /// An instance of representing the encoded coordinates. + /// + /// + /// Thrown when is empty. + /// + /// + /// Thrown when the internal encoding buffer cannot accommodate the encoded value. + /// + /// + /// Thrown when is canceled. + /// + public TPolyline Encode( + ReadOnlySpan coordinates, + PolylineEncodingOptions? options, + CancellationToken cancellationToken) { + const string OperationName = nameof(Encode); + + _logger.LogOperationStartedDebug(OperationName); + + Debug.Assert(coordinates.Length >= 0, "Count must be non-negative."); + + if (coordinates.Length < 1) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogEmptyArgumentWarning(nameof(coordinates)); + ExceptionGuard.ThrowArgumentCannotBeEmptyEnumerationMessage(nameof(coordinates)); + } + + int width = _formatter.Width; + int length = GetMaxBufferLength(coordinates.Length, width); + + char[]? temp = length <= _options.StackAllocLimit + ? null + : ArrayPool.Shared.Rent(length); + + Span buffer = temp is null ? stackalloc char[length] : temp.AsSpan(0, length); + + int position = 0; + long[] previous = new long[width]; + long[] values = new long[width]; + + SeedPrevious(previous, options); + + try { + for (int i = 0; i < coordinates.Length; i++) { + cancellationToken.ThrowIfCancellationRequested(); + + _formatter.GetValues(coordinates[i], values.AsSpan()); + + for (int j = 0; j < width; j++) { + long current = values[j]; + long delta = current - previous[j]; + previous[j] = current; + + if (!PolylineEncoding.TryWriteValue(delta, buffer, ref position)) { + _logger.LogOperationFailedDebug(OperationName); + _logger.LogCannotWriteValueToBufferWarning(position, i); + ExceptionGuard.ThrowCouldNotWriteEncodedValueToBuffer(); + } + } + } + + string encodedResult = buffer[..position].ToString(); + + _logger.LogOperationFinishedDebug(OperationName); + + return _formatter.Write(encodedResult.AsMemory()); + } finally { + if (temp is not null) { + ArrayPool.Shared.Return(temp); + } + } + } + + private void SeedPrevious(long[] previous, PolylineEncodingOptions? options) { + int width = _formatter.Width; + + if (options is { HasPrevious: true }) { + _formatter.GetValues(options.Previous, previous.AsSpan()); + } else { + for (int j = 0; j < width; j++) { + previous[j] = _formatter.GetBaseline(j); + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetMaxBufferLength(int count, int valuesPerItem) { Debug.Assert(count > 0, "Count must be greater than zero."); diff --git a/src/PolylineAlgorithm/PolylineEncodingOptions.cs b/src/PolylineAlgorithm/PolylineEncodingOptions.cs new file mode 100644 index 00000000..aa331b04 --- /dev/null +++ b/src/PolylineAlgorithm/PolylineEncodingOptions.cs @@ -0,0 +1,52 @@ +// +// Copyright © Pete Sramek. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace PolylineAlgorithm; + +/// +/// Per-call options for a chunked encoding operation. +/// +/// The coordinate type understood by the formatter. +/// +/// Pass an instance of this class to the chunked +/// overload to control +/// the delta baseline used at the start of each chunk. When is +/// the formatter's built-in baseline (or zero) is used, which is equivalent +/// to the existing default behaviour. +/// +public sealed class PolylineEncodingOptions { + private readonly TCoordinate _previous; + + /// + /// Initializes a new instance of with no + /// previous coordinate (formatter default baseline will be used). + /// + public PolylineEncodingOptions() { } + + /// + /// Initializes a new instance of with the + /// specified previous coordinate used to seed the delta baseline. + /// + /// + /// The last coordinate of the previous chunk, used to seed the delta baseline. + /// + public PolylineEncodingOptions(TCoordinate previous) { + _previous = previous; + HasPrevious = true; + } + + /// + /// Gets a value indicating whether a previous coordinate has been supplied to seed the delta + /// baseline. When the formatter's built-in baseline (or zero) is used, + /// which is equivalent to the existing default behaviour. + /// + public bool HasPrevious { get; } + + /// + /// Gets the last coordinate of the previous chunk, used to seed the delta baseline. + /// Only meaningful when is . + /// + public TCoordinate Previous => _previous; +} diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index d49ab754..6c7fd426 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -56,3 +56,22 @@ static PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpa static PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan polyline) -> void static PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan polyline) -> void virtual PolylineAlgorithm.PolylineItemFactory.Invoke(System.ReadOnlySpan values) -> T +PolylineAlgorithm.Abstraction.IChunkedPolylineDecoder +PolylineAlgorithm.Abstraction.IChunkedPolylineDecoder.Decode(TPolyline polyline, PolylineAlgorithm.PolylineDecodingOptions? options, System.Threading.CancellationToken cancellationToken) -> System.Collections.Generic.IEnumerable! +PolylineAlgorithm.Abstraction.IChunkedPolylineEncoder +PolylineAlgorithm.Abstraction.IChunkedPolylineEncoder.Encode(System.ReadOnlySpan coordinates, PolylineAlgorithm.PolylineEncodingOptions? options, System.Threading.CancellationToken cancellationToken) -> TPolyline +PolylineAlgorithm.PolylineDecodingOptions +PolylineAlgorithm.PolylineDecodingOptions.HasPrevious.get -> bool +PolylineAlgorithm.PolylineDecodingOptions.PolylineDecodingOptions() -> void +PolylineAlgorithm.PolylineDecodingOptions.PolylineDecodingOptions(TCoordinate previous) -> void +PolylineAlgorithm.PolylineDecodingOptions.Previous.get -> TCoordinate +PolylineAlgorithm.PolylineDecoder.Decode(TPolyline polyline, PolylineAlgorithm.PolylineDecodingOptions? options, System.Threading.CancellationToken cancellationToken) -> System.Collections.Generic.IEnumerable! +PolylineAlgorithm.PolylineEncodingOptions +PolylineAlgorithm.PolylineEncodingOptions.HasPrevious.get -> bool +PolylineAlgorithm.PolylineEncodingOptions.PolylineEncodingOptions() -> void +PolylineAlgorithm.PolylineEncodingOptions.PolylineEncodingOptions(TCoordinate previous) -> void +PolylineAlgorithm.PolylineEncodingOptions.Previous.get -> TCoordinate +PolylineAlgorithm.PolylineEncoder.Encode(System.ReadOnlySpan coordinates, PolylineAlgorithm.PolylineEncodingOptions? options, System.Threading.CancellationToken cancellationToken) -> TPolyline +static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IChunkedPolylineDecoder! decoder, string! polyline, PolylineAlgorithm.PolylineDecodingOptions? options) -> System.Collections.Generic.IEnumerable! +static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IChunkedPolylineEncoder! encoder, System.Collections.Generic.List! coordinates, PolylineAlgorithm.PolylineEncodingOptions? options) -> TPolyline +static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IChunkedPolylineEncoder! encoder, TCoordinate[]! coordinates, PolylineAlgorithm.PolylineEncodingOptions? options) -> TPolyline diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs index 97d2edbf..f833e9b2 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs @@ -173,4 +173,154 @@ public void Decode_Without_WithCreate_Throws_InvalidOperationException() { Assert.ThrowsExactly( () => decoder.Decode(polyline).ToList()); } + + // ------------------------------------------------------------------ + // Chunked Decode — options overload + // ------------------------------------------------------------------ + + /// + /// Tests that the chunked overload with null options produces the same result as the standard + /// overload. + /// + [TestMethod] + public void Decode_Chunked_With_Null_Options_Produces_Same_Result_As_Standard() { + // Arrange + PolylineDecoder decoder = CreateDecoder(); + string polyline = StaticValueProvider.Valid.GetPolyline(); + + // Act + List<(double Lat, double Lon)> standard = decoder.Decode(polyline).ToList(); + List<(double Lat, double Lon)> chunked = decoder.Decode(polyline, null, CancellationToken.None).ToList(); + + // Assert + Assert.AreEqual(standard.Count, chunked.Count); + for (int i = 0; i < standard.Count; i++) { + Assert.AreEqual(standard[i].Lat, chunked[i].Lat, 1e-5); + Assert.AreEqual(standard[i].Lon, chunked[i].Lon, 1e-5); + } + } + + /// + /// Tests that chunked decoding with a Previous coordinate seeds the accumulated state + /// correctly, producing a result different from standard decoding of the same polyline. + /// + [TestMethod] + public void Decode_Chunked_With_Previous_Seeds_Accumulated_State() { + // Arrange — build a chunked polyline where chunk B is relative to the last of chunk A + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", c => c.Lat) + .AddValue("lon", c => c.Lon) + .WithCreate(static v => (v[0], v[1])) + .ForPolyline(_write, _read) + .Build(); + + PolylineOptions<(double Lat, double Lon), string> options = new(formatter); + PolylineEncoder<(double Lat, double Lon), string> encoder = new(options); + PolylineDecoder decoder = new(options); + + (double Lat, double Lon)[] chunkA = [(38.5, -120.2), (40.7, -120.95)]; + (double Lat, double Lon)[] chunkB = [(43.252, -126.453)]; + + string polylineB = encoder.Encode( + chunkB.AsSpan(), + new PolylineEncodingOptions<(double Lat, double Lon)>(chunkA[^1]), + CancellationToken.None); + + // Act + (double Lat, double Lon) decodedWithSeed = decoder.Decode( + polylineB, + new PolylineDecodingOptions<(double Lat, double Lon)>(chunkA[^1]), + CancellationToken.None).First(); + + // Assert — decoded value should match the original chunkB[0] + Assert.AreEqual(chunkB[0].Lat, decodedWithSeed.Lat, 1e-5); + Assert.AreEqual(chunkB[0].Lon, decodedWithSeed.Lon, 1e-5); + } + + /// + /// Tests chunked encode + chunked decode round-trip: splitting a sequence into two chunks, + /// encoding with chaining, then decoding each chunk independently with Previous seed produces + /// the original sequence. + /// + [TestMethod] + public void Decode_Chunked_RoundTrip_Reproduces_Full_Sequence() { + // Arrange + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", c => c.Lat) + .AddValue("lon", c => c.Lon) + .WithCreate(static v => (v[0], v[1])) + .ForPolyline(_write, _read) + .Build(); + + PolylineOptions<(double Lat, double Lon), string> options = new(formatter); + PolylineEncoder<(double Lat, double Lon), string> encoder = new(options); + PolylineDecoder decoder = new(options); + + (double Lat, double Lon)[] all = [ + (38.5, -120.2), + (40.7, -120.95), + (43.252, -126.453), + (47.6, -122.3), + ]; + + (double Lat, double Lon)[] chunkA = all[..2]; + (double Lat, double Lon)[] chunkB = all[2..]; + + // Encode chunked + string polylineA = encoder.Encode(chunkA.AsSpan()); + string polylineB = encoder.Encode( + chunkB.AsSpan(), + new PolylineEncodingOptions<(double Lat, double Lon)>(chunkA[^1]), + CancellationToken.None); + + // Decode chunked + List<(double Lat, double Lon)> decodedA = decoder.Decode(polylineA).ToList(); + List<(double Lat, double Lon)> decodedB = decoder.Decode( + polylineB, + new PolylineDecodingOptions<(double Lat, double Lon)>(decodedA[^1]), + CancellationToken.None).ToList(); + + List<(double Lat, double Lon)> combined = [.. decodedA, .. decodedB]; + + // Assert + Assert.AreEqual(all.Length, combined.Count); + for (int i = 0; i < all.Length; i++) { + Assert.AreEqual(all[i].Lat, combined[i].Lat, 1e-5); + Assert.AreEqual(all[i].Lon, combined[i].Lon, 1e-5); + } + } + + /// + /// Tests that a null polyline throws when using the + /// chunked overload. + /// + [TestMethod] + public void Decode_Chunked_With_Null_Polyline_Throws_ArgumentNullException() { + // Arrange + PolylineDecoder decoder = CreateDecoder(); + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsExactly( + () => decoder.Decode(null!, null, CancellationToken.None).ToList()); + Assert.AreEqual("polyline", ex.ParamName); + } + + /// + /// Tests that a pre-cancelled token throws in the + /// chunked overload. + /// + [TestMethod] + public void Decode_Chunked_With_Pre_Cancelled_Token_Throws_OperationCanceledException() { + // Arrange + PolylineDecoder decoder = CreateDecoder(); + string polyline = StaticValueProvider.Valid.GetPolyline(); + using CancellationTokenSource cts = new(); + cts.Cancel(); + + // Act & Assert + Assert.ThrowsExactly( + () => decoder.Decode(polyline, null, cts.Token).ToList()); + } } diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs index 62c38ea2..9875ebe6 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs @@ -221,4 +221,136 @@ public void Encode_RoundTrip_Produces_Original_Coordinates() { Assert.AreEqual(original[i].Lon, decoded[i].Lon, 1e-5); } } + + // ------------------------------------------------------------------ + // Chunked Encode — options overload + // ------------------------------------------------------------------ + + /// + /// Tests that the chunked overload with null options produces the same result as the standard + /// overload (no baseline difference). + /// + [TestMethod] + public void Encode_Chunked_With_Null_Options_Produces_Same_Result_As_Standard() { + // Arrange + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); + (double, double)[] coordinates = [(38.5, -120.2), (40.7, -120.95), (43.252, -126.453)]; + + // Act + string standard = encoder.Encode(coordinates.AsSpan()); + string chunked = encoder.Encode(coordinates.AsSpan(), null, CancellationToken.None); + + // Assert + Assert.AreEqual(standard, chunked); + } + + /// + /// Tests that chunked encoding with a Previous coordinate seeds the delta baseline correctly, + /// producing a result different from standard encoding of the same coordinates. + /// + [TestMethod] + public void Encode_Chunked_With_Previous_Seeds_Delta_Baseline() { + // Arrange + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); + (double Lat, double Lon)[] chunkA = [(38.5, -120.2), (40.7, -120.95)]; + (double Lat, double Lon)[] chunkB = [(43.252, -126.453)]; + + // Act — encode chunk B starting from zero (standard) and from chunkA's last point + string standardB = encoder.Encode(chunkB.AsSpan()); + string chunkedB = encoder.Encode( + chunkB.AsSpan(), + new PolylineEncodingOptions<(double Lat, double Lon)>(chunkA[^1]), + CancellationToken.None); + + // Assert — the two polylines must differ because the first delta changes + Assert.AreNotEqual(standardB, chunkedB); + } + + /// + /// Tests that encoding in two chunks produces a polyline that, when concatenated, decodes to + /// the same coordinate sequence as encoding the full sequence at once. + /// + [TestMethod] + public void Encode_Chunked_Concatenated_Decodes_To_Full_Sequence() { + // Arrange + PolylineFormatter<(double Lat, double Lon), string> formatter = + FormatterBuilder<(double Lat, double Lon), string>.Create() + .AddValue("lat", c => c.Lat) + .AddValue("lon", c => c.Lon) + .WithCreate(static v => (v[0], v[1])) + .ForPolyline(_write, _read) + .Build(); + + PolylineOptions<(double Lat, double Lon), string> options = new(formatter); + PolylineEncoder<(double Lat, double Lon), string> encoder = new(options); + PolylineDecoder decoder = new(options); + + (double Lat, double Lon)[] all = [ + (38.5, -120.2), + (40.7, -120.95), + (43.252, -126.453), + (47.6, -122.3), + ]; + + (double Lat, double Lon)[] chunkA = all[..2]; + (double Lat, double Lon)[] chunkB = all[2..]; + + // Act + string polylineA = encoder.Encode(chunkA.AsSpan()); + string polylineB = encoder.Encode( + chunkB.AsSpan(), + new PolylineEncodingOptions<(double Lat, double Lon)>(chunkA[^1]), + CancellationToken.None); + + string concatenated = polylineA + polylineB; + string fullEncoding = encoder.Encode(all.AsSpan()); + + (double Lat, double Lon)[] decodedConcatenated = [.. decoder.Decode(concatenated)]; + (double Lat, double Lon)[] decodedFull = [.. decoder.Decode(fullEncoding)]; + + // Assert + Assert.AreEqual(concatenated, fullEncoding, + "Chunked-then-concatenated polyline should equal the full-sequence encoding."); + Assert.AreEqual(all.Length, decodedConcatenated.Length); + for (int i = 0; i < all.Length; i++) { + Assert.AreEqual(all[i].Lat, decodedConcatenated[i].Lat, 1e-5); + Assert.AreEqual(all[i].Lon, decodedConcatenated[i].Lon, 1e-5); + Assert.AreEqual(decodedFull[i].Lat, decodedConcatenated[i].Lat, 1e-5); + Assert.AreEqual(decodedFull[i].Lon, decodedConcatenated[i].Lon, 1e-5); + } + } + + /// + /// Tests that an empty span still throws when using the + /// chunked overload. + /// + [TestMethod] + public void Encode_Chunked_With_Empty_Span_Throws_ArgumentException() { + // Arrange + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); + + // Act & Assert + Assert.ThrowsExactly( + () => encoder.Encode( + ReadOnlySpan<(double, double)>.Empty, + new PolylineEncodingOptions<(double Lat, double Lon)>(), + CancellationToken.None)); + } + + /// + /// Tests that a pre-cancelled token throws in the + /// chunked overload. + /// + [TestMethod] + public void Encode_Chunked_With_Pre_Cancelled_Token_Throws_OperationCanceledException() { + // Arrange + PolylineEncoder<(double Lat, double Lon), string> encoder = CreateEncoder(); + using CancellationTokenSource cts = new(); + cts.Cancel(); + (double, double)[] coordinates = [(0.0, 0.0), (1.0, 1.0)]; + + // Act & Assert + Assert.ThrowsExactly( + () => encoder.Encode(coordinates.AsSpan(), null, cts.Token)); + } } From 74991690d05c5e903e22dac30019ba9efd70b99a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:58:49 +0000 Subject: [PATCH 20/22] docs: clarify HasPrevious documentation in PolylineEncodingOptions Agent-Logs-Url: https://github.com/petesramek/polyline-algorithm-csharp/sessions/49b11574-fbb3-42e8-b1f2-8af7fc403268 Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com> --- src/PolylineAlgorithm/PolylineEncodingOptions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PolylineAlgorithm/PolylineEncodingOptions.cs b/src/PolylineAlgorithm/PolylineEncodingOptions.cs index aa331b04..d9cc299f 100644 --- a/src/PolylineAlgorithm/PolylineEncodingOptions.cs +++ b/src/PolylineAlgorithm/PolylineEncodingOptions.cs @@ -39,8 +39,9 @@ public PolylineEncodingOptions(TCoordinate previous) { /// /// Gets a value indicating whether a previous coordinate has been supplied to seed the delta - /// baseline. When the formatter's built-in baseline (or zero) is used, - /// which is equivalent to the existing default behaviour. + /// baseline. When the formatter's built-in baseline is used as the + /// starting point (which defaults to zero when no baseline has been configured), equivalent to + /// the existing default behaviour. /// public bool HasPrevious { get; } From b895ea5d6f3f6ca7d042dc5d6a4b8f7a31339222 Mon Sep 17 00:00:00 2001 From: Pete Sramek <2333452+petesramek@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:13:34 +0200 Subject: [PATCH 21/22] updates --- .../PolylineDecoderBenchmark.cs | 6 +- .../PolylineEncoderBenchmark.cs | 23 +-- .../Program.cs | 4 +- .../Program.cs | 4 +- .../Abstraction/IChunkedPolylineDecoder.cs | 52 ----- .../Abstraction/IChunkedPolylineEncoder.cs | 47 ----- .../Abstraction/IPolylineDecoder.cs | 4 +- .../Abstraction/IPolylineEncoder.cs | 2 +- .../Abstraction/IPolylineFormatter.cs | 20 +- .../Extensions/PolylineDecoderExtensions.cs | 134 ------------- .../Extensions/PolylineEncoderExtensions.cs | 144 ++----------- src/PolylineAlgorithm/FormatterBuilder.cs | 52 ++--- .../Internal/CoordinateDelta.cs | 72 ------- .../Internal/FormatterRule.cs | 2 +- src/PolylineAlgorithm/PolylineDecoder.cs | 101 ++-------- .../PolylineDecodingOptions.cs | 27 ++- src/PolylineAlgorithm/PolylineEncoder.cs | 39 ++-- .../PolylineEncodingOptions.cs | 16 +- src/PolylineAlgorithm/PolylineFormatter.cs | 26 +-- src/PolylineAlgorithm/PolylineItemFactory.cs | 2 +- src/PolylineAlgorithm/PolylineOptions.cs | 16 +- src/PolylineAlgorithm/PublicAPI.Unshipped.txt | 77 ------- src/PolylineAlgorithm/README.md | 10 +- .../AbstractPolylineDecoderTests.cs | 56 ++---- .../AbstractPolylineEncoderTests.cs | 16 +- .../PolylineDecoderExtensionsTests.cs | 189 ------------------ .../PolylineEncoderExtensionsTests.cs | 51 +---- .../Internal/CoordinateDeltaTests.cs | 136 ------------- .../PolylineEncodingOptionsBuilderTests.cs | 30 +-- .../PolylineFormatterTests.cs | 50 ++--- .../RandomValueProvider.cs | 2 +- 31 files changed, 211 insertions(+), 1199 deletions(-) delete mode 100644 src/PolylineAlgorithm/Abstraction/IChunkedPolylineDecoder.cs delete mode 100644 src/PolylineAlgorithm/Abstraction/IChunkedPolylineEncoder.cs delete mode 100644 src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs delete mode 100644 src/PolylineAlgorithm/Internal/CoordinateDelta.cs delete mode 100644 tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs delete mode 100644 tests/PolylineAlgorithm.Tests/Internal/CoordinateDeltaTests.cs diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs index e6a1ed76..43804520 100644 --- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs +++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs @@ -11,7 +11,7 @@ namespace PolylineAlgorithm.Benchmarks; using PolylineAlgorithm.Utility; /// -/// Benchmarks for . +/// Benchmarks for . /// public class PolylineDecoderBenchmark { private readonly Consumer _consumer = new(); @@ -57,8 +57,8 @@ public class PolylineDecoderBenchmark { FormatterBuilder<(double Latitude, double Longitude), T>.Create() .AddValue("lat", static c => c.Latitude) .AddValue("lon", static c => c.Longitude) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(write, read) + .WithValueFactory(static v => (v[0], v[1])) + .WithReaderWriter(write, read) .Build(); private static PolylineDecoder CreateStringDecoder() { diff --git a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs index f7471463..2e8d09f1 100644 --- a/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs +++ b/benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs @@ -3,12 +3,10 @@ namespace PolylineAlgorithm.Benchmarks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Engines; using PolylineAlgorithm; -using PolylineAlgorithm.Extensions; using PolylineAlgorithm.Utility; -using System.Collections.Generic; /// -/// Benchmarks for . +/// Benchmarks for . /// public class PolylineEncoderBenchmark { private readonly Consumer _consumer = new(); @@ -20,11 +18,6 @@ public class PolylineEncoderBenchmark { public int CoordinatesCount { get; set; } #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - /// - /// Coordinates as list. - /// - public List<(double Latitude, double Longitude)> List { get; private set; } - /// /// Coordinates as array. /// @@ -47,7 +40,7 @@ public class PolylineEncoderBenchmark { FormatterBuilder<(double Latitude, double Longitude), string>.Create() .AddValue("lat", static c => c.Latitude) .AddValue("lon", static c => c.Longitude) - .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .WithReaderWriter(static m => new string(m.Span), static s => s.AsMemory()) .Build(); return new PolylineEncoder<(double Latitude, double Longitude), string>( @@ -59,8 +52,7 @@ public class PolylineEncoderBenchmark { ///
[GlobalSetup] public void SetupData() { - List = [.. RandomValueProvider.GetCoordinates(CoordinatesCount)]; - Array = [.. List]; + Array = [.. RandomValueProvider.GetCoordinates(CoordinatesCount)]; Memory = Array.AsMemory(); } @@ -81,13 +73,4 @@ public void PolylineEncoder_Encode_Array() { var polyline = _encoder.Encode(Array); _consumer.Consume(polyline); } - - /// - /// Benchmark: encode coordinates from list. - /// - [Benchmark] - public void PolylineEncoder_Encode_List() { - var polyline = _encoder.Encode(List); - _consumer.Consume(polyline); - } } diff --git a/samples/PolylineAlgorithm.NetTopologySuite.Sample/Program.cs b/samples/PolylineAlgorithm.NetTopologySuite.Sample/Program.cs index 3f91db95..766a0771 100644 --- a/samples/PolylineAlgorithm.NetTopologySuite.Sample/Program.cs +++ b/samples/PolylineAlgorithm.NetTopologySuite.Sample/Program.cs @@ -15,8 +15,8 @@ public static void Main(string[] args) { .AddValue("lat", static p => p.Y) .AddValue("lon", static p => p.X) // The formatter automatically denormalizes scaled values, so v[0] = latitude, v[1] = longitude. - .WithCreate(static v => new Point(x: v[1], y: v[0])) - .ForPolyline( + .WithValueFactory(static v => new Point(x: v[1], y: v[0])) + .WithReaderWriter( static m => m.IsEmpty ? string.Empty : new string(m.Span), static s => s.AsMemory()) .Build(); diff --git a/samples/PolylineAlgorithm.SensorData.Sample/Program.cs b/samples/PolylineAlgorithm.SensorData.Sample/Program.cs index 05a0637a..c7fe8f62 100644 --- a/samples/PolylineAlgorithm.SensorData.Sample/Program.cs +++ b/samples/PolylineAlgorithm.SensorData.Sample/Program.cs @@ -21,10 +21,10 @@ public static void Main(string[] args) { .SetBaseline(TimestampBaseEpoch) .AddValue("temperature", static r => r.Temperature, precision: 5) // The formatter automatically denormalizes: v[0] = Unix seconds, v[1] = temperature. - .WithCreate(static v => new SensorReading( + .WithValueFactory(static v => new SensorReading( DateTimeOffset.FromUnixTimeSeconds((long)v[0]), v[1])) - .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .WithReaderWriter(static m => new string(m.Span), static s => s.AsMemory()) .Build(); PolylineOptions options = new(formatter); diff --git a/src/PolylineAlgorithm/Abstraction/IChunkedPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/IChunkedPolylineDecoder.cs deleted file mode 100644 index d69849a1..00000000 --- a/src/PolylineAlgorithm/Abstraction/IChunkedPolylineDecoder.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.Abstraction; - -using System.Collections.Generic; -using System.Threading; - -/// -/// Provides per-call options-based chunked (stateless-continuation) decoding without inheriting the -/// covariant . Implement both interfaces on the -/// concrete decoder class to support both the standard and chunked overloads. -/// -/// The encoded polyline type. -/// The coordinate type. -/// -/// Use this interface when you need to decode a polyline that was produced by chunked encoding. -/// Pass set to the last coordinate of the -/// preceding decoded chunk to seed the accumulated-delta state correctly. -/// -public interface IChunkedPolylineDecoder { - /// - /// Decodes an encoded into a sequence of coordinates, applying - /// the per-call to control the accumulated-delta seed. - /// - /// The encoded polyline to decode. Must not be . - /// - /// Per-call options that control the starting accumulated-delta seed. Pass - /// or an instance with - /// set to to start from zero (the default behaviour). - /// - /// A token that can be used to cancel the operation. - /// - /// An of representing the decoded - /// coordinates. - /// - /// - /// Thrown when is . - /// - /// - /// Thrown when the polyline format is invalid or malformed. - /// - /// - /// Thrown when is canceled. - /// - IEnumerable Decode( - TPolyline polyline, - PolylineDecodingOptions? options, - CancellationToken cancellationToken); -} diff --git a/src/PolylineAlgorithm/Abstraction/IChunkedPolylineEncoder.cs b/src/PolylineAlgorithm/Abstraction/IChunkedPolylineEncoder.cs deleted file mode 100644 index a1ee0b44..00000000 --- a/src/PolylineAlgorithm/Abstraction/IChunkedPolylineEncoder.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.Abstraction; - -using System; -using System.Collections.Generic; -using System.Threading; - -/// -/// Extends with a per-call options overload that -/// supports chunked (stateless-continuation) encoding. -/// -/// The coordinate type. -/// The encoded polyline type. -/// -/// Use this interface when you need to encode a large coordinate sequence in independent chunks that -/// can be concatenated into a single valid polyline. Pass -/// set to the last coordinate of the -/// preceding chunk to seed the delta baseline correctly. -/// -public interface IChunkedPolylineEncoder : IPolylineEncoder { - /// - /// Encodes a sequence of geographic coordinates into an encoded polyline, applying the per-call - /// to control the delta baseline. - /// - /// The collection of coordinates to encode. - /// - /// Per-call options that control the starting delta baseline. Pass - /// or an instance with - /// set to to use the formatter's default baseline. - /// - /// A token that can be used to cancel the operation. - /// - /// An instance of representing the encoded coordinates. - /// - /// Thrown when is empty. - /// - /// Thrown when is canceled. - /// - TPolyline Encode( - ReadOnlySpan coordinates, - PolylineEncodingOptions? options, - CancellationToken cancellationToken); -} diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs b/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs index 114f88fd..eb57148c 100644 --- a/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs +++ b/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs @@ -19,7 +19,7 @@ namespace PolylineAlgorithm.Abstraction; /// The coordinate type returned by the decoder. Typical implementations return a struct or class that /// contains latitude and longitude (for example a LatLng type or a ValueTuple<double,double>). /// -public interface IPolylineDecoder { +public interface IPolylineDecoder { /// /// Decodes the specified encoded polyline into an ordered sequence of geographic coordinates. /// The sequence preserves the original vertex order encoded by the . @@ -45,5 +45,5 @@ public interface IPolylineDecoder { /// /// Thrown when the provided requests cancellation. /// - IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default); + IEnumerable Decode(TPolyline polyline, PolylineDecodingOptions? options = null, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs b/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs index 9cea260b..724f4f73 100644 --- a/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs +++ b/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs @@ -73,5 +73,5 @@ public interface IPolylineEncoder { /// /// Thrown if the operation is canceled via . /// - TPolyline Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default); + TPolyline Encode(ReadOnlySpan coordinates, PolylineEncodingOptions? options = null, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs index ad31460c..c88a4aab 100644 --- a/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs +++ b/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs @@ -8,19 +8,19 @@ namespace PolylineAlgorithm.Abstraction; using System; /// -/// Defines how to extract and scale values from a for encoding, -/// reconstruct a from scaled values for decoding, +/// Defines how to extract and scale values from a for encoding, +/// reconstruct a from scaled values for decoding, /// produce a from an encoded character buffer, and extract that buffer /// back from a . /// -/// The coordinate or item type. For example a struct with Latitude/Longitude. +/// The coordinate or item type. For example a struct with Latitude/Longitude. /// The polyline surface type. For example or /// of . /// -/// Use to build a -/// that implements this interface. +/// Use to build a +/// that implements this interface. /// -public interface IPolylineFormatter { +public interface IPolylineFormatter { /// /// Gets the number of values (columns) per encoded item. /// This is the required length of the passed to @@ -45,7 +45,7 @@ public interface IPolylineFormatter { /// /// Output buffer that receives the scaled integer values. Its length must equal . /// - void GetValues(TCoordinate item, Span values); + void GetValues(TValue item, Span values); /// /// Creates a from the encoded character buffer produced by the encoder. @@ -62,7 +62,7 @@ public interface IPolylineFormatter { ReadOnlyMemory Read(TPolyline polyline); /// - /// Reconstructs a from the given accumulated scaled integer values. + /// Reconstructs a from the given accumulated scaled integer values. /// Called once per decoded item in the decoding loop. Implementations are responsible for /// denormalizing the raw scaled integers (e.g. dividing by the precision factor and adding back /// any baseline) before constructing the item. @@ -72,6 +72,6 @@ public interface IPolylineFormatter { /// the same column position as in . These are the direct output of the /// delta-accumulation loop in the decoder before any denormalization is applied. /// - /// A reconstructed from . - TCoordinate CreateItem(ReadOnlySpan values); + /// A reconstructed from . + TValue CreateItem(ReadOnlySpan values); } diff --git a/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs b/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs deleted file mode 100644 index 8da3dc72..00000000 --- a/src/PolylineAlgorithm/Extensions/PolylineDecoderExtensions.cs +++ /dev/null @@ -1,134 +0,0 @@ -// -// 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; - -using PolylineAlgorithm.Abstraction; -using PolylineAlgorithm.Internal.Diagnostics; -using System; -using System.Collections.Generic; - -/// -/// Provides extension methods for the interface to facilitate decoding encoded polylines. -/// -public static class PolylineDecoderExtensions { - /// - /// Decodes an encoded polyline represented as a character array into a sequence of geographic coordinates. - /// - /// The coordinate type returned by the decoder. - /// - /// The instance used to perform the decoding operation. - /// - /// - /// The encoded polyline as a character array to decode. The array is converted to a string internally. - /// - /// - /// An of containing the decoded coordinate pairs. - /// - /// - /// Thrown when or is . - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public static IEnumerable Decode(this IPolylineDecoder decoder, char[] polyline) { - if (decoder is null) { - ExceptionGuard.ThrowArgumentNull(nameof(decoder)); - } - - if (polyline is null) { - ExceptionGuard.ThrowArgumentNull(nameof(polyline)); - } - - return decoder.Decode(new string(polyline)); - } - - /// - /// Decodes an encoded polyline represented as a read-only memory of characters into a sequence of geographic coordinates. - /// - /// The coordinate type returned by the decoder. - /// - /// The instance used to perform the decoding operation. - /// - /// - /// The encoded polyline as a read-only memory of characters to decode. The memory is converted to a string internally. - /// - /// - /// An of containing the decoded coordinate pairs. - /// - /// - /// Thrown when is . - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public static IEnumerable Decode(this IPolylineDecoder decoder, ReadOnlyMemory polyline) { - if (decoder is null) { - ExceptionGuard.ThrowArgumentNull(nameof(decoder)); - } - - return decoder.Decode(polyline.ToString()); - } - - /// - /// Decodes an encoded polyline string into a sequence of geographic coordinates, - /// using a decoder that accepts of . - /// - /// The coordinate type returned by the decoder. - /// - /// The instance used to perform the decoding operation. - /// - /// - /// The encoded polyline string to decode. The string is converted to internally. - /// - /// - /// An of containing the decoded coordinate pairs. - /// - /// - /// Thrown when or is . - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public static IEnumerable Decode(this IPolylineDecoder, TValue> decoder, string polyline) { - if (decoder is null) { - ExceptionGuard.ThrowArgumentNull(nameof(decoder)); - } - - if (polyline is null) { - ExceptionGuard.ThrowArgumentNull(nameof(polyline)); - } - - return decoder.Decode(polyline.AsMemory()); - } - - /// - /// Decodes an encoded polyline string into a sequence of geographic coordinates, applying per-call - /// to seed the accumulated-delta state. - /// - /// The coordinate type returned by the decoder. - /// The chunked decoder instance. - /// The encoded polyline string to decode. - /// - /// Per-call options that control the accumulated-delta seed. Pass to start - /// from zero (the default behaviour). - /// - /// - /// An of containing the decoded - /// coordinate pairs. - /// - /// - /// Thrown when or is . - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public static IEnumerable Decode( - this IChunkedPolylineDecoder decoder, - string polyline, - PolylineDecodingOptions? options) { - if (decoder is null) { - ExceptionGuard.ThrowArgumentNull(nameof(decoder)); - } - - if (polyline is null) { - ExceptionGuard.ThrowArgumentNull(nameof(polyline)); - } - - return decoder.Decode(polyline, options, CancellationToken.None); - } -} diff --git a/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs b/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs index 15b15415..460eb99b 100644 --- a/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs +++ b/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright © Pete Sramek. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for full license information. // @@ -8,153 +8,41 @@ namespace PolylineAlgorithm.Extensions; using PolylineAlgorithm.Abstraction; using PolylineAlgorithm.Internal.Diagnostics; using System; -using System.Collections.Generic; -#if NET5_0_OR_GREATER -using System.Runtime.InteropServices; -#endif /// -/// Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines. +/// Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines. /// public static class PolylineEncoderExtensions { /// - /// Encodes a of instances into an encoded polyline. + /// Encodes an array of instances into an encoded polyline. /// - /// The type that represents a geographic coordinate to encode. + /// The type that represents a geographic coordinate to encode. /// The type that represents the encoded polyline output. /// - /// The instance used to perform the encoding operation. + /// The instance used to perform the encoding operation. /// - /// - /// The list of objects to encode. + /// + /// The array of objects to encode. /// /// /// A instance representing the encoded polyline for the provided coordinates. /// /// - /// Thrown when or is . - /// - [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.")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public static TPolyline Encode(this IPolylineEncoder encoder, List coordinates) { - if (encoder is null) { - ExceptionGuard.ThrowArgumentNull(nameof(encoder)); - } - - if (coordinates is null) { - ExceptionGuard.ThrowArgumentNull(nameof(coordinates)); - } - -#if NET5_0_OR_GREATER - return encoder.Encode(CollectionsMarshal.AsSpan(coordinates)); -#else - return encoder.Encode([.. coordinates]); -#endif - } - - - /// - /// Encodes an array of instances into an encoded polyline. - /// - /// The type that represents a geographic coordinate to encode. - /// The type that represents the encoded polyline output. - /// - /// The instance used to perform the encoding operation. - /// - /// - /// The array of objects to encode. - /// - /// - /// A instance representing the encoded polyline for the provided coordinates. - /// - /// - /// Thrown when or is . - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public static TPolyline Encode(this IPolylineEncoder encoder, TCoordinate[] coordinates) { - if (encoder is null) { - ExceptionGuard.ThrowArgumentNull(nameof(encoder)); - } - - if (coordinates is null) { - ExceptionGuard.ThrowArgumentNull(nameof(coordinates)); - } - - return encoder.Encode(coordinates.AsSpan()); - } - - /// - /// Encodes a of instances into an encoded - /// polyline, applying per-call to control the delta baseline. - /// - /// The type that represents a geographic coordinate to encode. - /// The type that represents the encoded polyline output. - /// The chunked encoder instance. - /// The list of coordinates to encode. - /// - /// Per-call options that control the starting delta baseline. Pass to use - /// the formatter's default baseline. - /// - /// - /// A representing the encoded polyline. - /// - /// - /// Thrown when or is . - /// - [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.")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public static TPolyline Encode( - this IChunkedPolylineEncoder encoder, - List coordinates, - PolylineEncodingOptions? options) { - if (encoder is null) { - ExceptionGuard.ThrowArgumentNull(nameof(encoder)); - } - - if (coordinates is null) { - ExceptionGuard.ThrowArgumentNull(nameof(coordinates)); - } - -#if NET5_0_OR_GREATER - return encoder.Encode(CollectionsMarshal.AsSpan(coordinates), options, CancellationToken.None); -#else - return encoder.Encode([.. coordinates], options, CancellationToken.None); -#endif - } - - /// - /// Encodes an array of instances into an encoded polyline, - /// applying per-call to control the delta baseline. - /// - /// The type that represents a geographic coordinate to encode. - /// The type that represents the encoded polyline output. - /// The chunked encoder instance. - /// The array of coordinates to encode. - /// - /// Per-call options that control the starting delta baseline. Pass to use - /// the formatter's default baseline. - /// - /// - /// A representing the encoded polyline. - /// - /// - /// Thrown when or is . + /// Thrown when or is . /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public static TPolyline Encode( - this IChunkedPolylineEncoder encoder, - TCoordinate[] coordinates, - PolylineEncodingOptions? options) { + public static TPolyline Encode( + this IPolylineEncoder encoder, + TValue[] values, + PolylineEncodingOptions? options = null, + CancellationToken cancellationToken = default) { if (encoder is null) { ExceptionGuard.ThrowArgumentNull(nameof(encoder)); } - if (coordinates is null) { - ExceptionGuard.ThrowArgumentNull(nameof(coordinates)); + if (values is null) { + ExceptionGuard.ThrowArgumentNull(nameof(values)); } - return encoder.Encode(coordinates.AsSpan(), options, CancellationToken.None); + return encoder.Encode(values.AsSpan(), options, cancellationToken); } } diff --git a/src/PolylineAlgorithm/FormatterBuilder.cs b/src/PolylineAlgorithm/FormatterBuilder.cs index 1189ee7f..8ea32dea 100644 --- a/src/PolylineAlgorithm/FormatterBuilder.cs +++ b/src/PolylineAlgorithm/FormatterBuilder.cs @@ -10,38 +10,38 @@ namespace PolylineAlgorithm; using System.Collections.Generic; /// -/// Provides a fluent builder for constructing a . +/// Provides a fluent builder for constructing a . /// -/// The coordinate or item type from which column values are extracted. +/// The coordinate or item type from which column values are extracted. /// The polyline surface type produced and consumed by the formatter. /// /// /// Use to obtain an instance, call once per column, /// optionally chain to set a reference baseline for the most-recently added column, -/// optionally chain to register a factory for the decoding direction, -/// call to supply the polyline surface delegates (required), then call -/// to produce the immutable . +/// optionally chain to register a factory for the decoding direction, +/// call to supply the polyline surface delegates (required), then call +/// to produce the immutable . /// /// -/// The builder is the only way to create a +/// The builder is the only way to create a /// — its constructor is internal. /// /// -public sealed class FormatterBuilder { - private readonly List> _rules = []; +public sealed class FormatterBuilder { + private readonly List> _rules = []; private readonly HashSet _names = new(StringComparer.Ordinal); - private PolylineItemFactory? _create; + private PolylineItemFactory? _create; private Func, TPolyline>? _write; private Func>? _read; private FormatterBuilder() { } /// - /// Creates a new instance. + /// Creates a new instance. /// /// A fresh builder with no rules and no polyline delegates. [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Factory method on a generic builder intentionally lives on the type so callers write FormatterBuilder.Create() without needing a separate non-generic factory class.")] - public static FormatterBuilder Create() => new(); + public static FormatterBuilder Create() => new(); /// /// Adds a column with the specified value selector and precision. @@ -51,7 +51,7 @@ private FormatterBuilder() { } /// /// /// A delegate that extracts the column's raw value from an item of type - /// . + /// . /// /// /// The number of decimal places to preserve. Each extracted value is multiplied by @@ -64,7 +64,7 @@ private FormatterBuilder() { } /// /// Thrown when is empty, or a rule with the same name already exists. /// - public FormatterBuilder AddValue(string name, Func selector, uint precision = 5) { + public FormatterBuilder AddValue(string name, Func selector, uint precision = 5) { if (name is null) { throw new ArgumentNullException(nameof(name)); } @@ -81,7 +81,7 @@ public FormatterBuilder AddValue(string name, Func(name, (long)Pow10.GetFactor(precision), selector)); + _rules.Add(new FormatterRule(name, (long)Pow10.GetFactor(precision), selector)); return this; } @@ -103,24 +103,24 @@ public FormatterBuilder AddValue(string name, Func before /// . /// - public FormatterBuilder SetBaseline(long baseline) { + public FormatterBuilder SetBaseline(long baseline) { if (_rules.Count == 0) { throw new InvalidOperationException("Cannot set a baseline when no rules have been added. Call AddValue first."); } var last = _rules[^1]; - _rules[^1] = new FormatterRule(last.Name, last.Factor, last.Select, baseline); + _rules[^1] = new FormatterRule(last.Name, last.Factor, last.Select, baseline); return this; } /// - /// Registers a factory delegate used to reconstruct a from + /// Registers a factory delegate used to reconstruct a from /// denormalized values during decoding. /// /// /// A delegate that accepts the denormalized values reconstructed from the - /// polyline and returns a . The formatter automatically divides + /// polyline and returns a . The formatter automatically divides /// each accumulated scaled integer by its precision factor and adds back any baseline configured /// via , so the span values match the original values supplied to the /// encoder. The span length always equals the number of columns added via . @@ -129,7 +129,7 @@ public FormatterBuilder SetBaseline(long baseline) { /// /// Thrown when is . /// - public FormatterBuilder WithCreate(PolylineItemFactory create) { + public FormatterBuilder WithValueFactory(PolylineItemFactory create) { if (create is null) { throw new ArgumentNullException(nameof(create)); } @@ -155,7 +155,7 @@ public FormatterBuilder WithCreate(PolylineItemFactory /// Thrown when or is . /// - public FormatterBuilder ForPolyline( + public FormatterBuilder WithReaderWriter( Func, TPolyline> write, Func> read) { if (write is null) { @@ -174,16 +174,16 @@ public FormatterBuilder ForPolyline( /// /// Bakes all added rules and delegates into a sealed, immutable - /// . + /// . /// /// - /// An immutable whose configuration can + /// An immutable whose configuration can /// no longer be changed. /// /// - /// Thrown when no rules have been added, or when has not been called. + /// Thrown when no rules have been added, or when has not been called. /// - public PolylineFormatter Build() { + public PolylineFormatter Build() { if (_rules.Count == 0) { throw new InvalidOperationException("At least one rule must be added before calling Build."); } @@ -191,9 +191,9 @@ public PolylineFormatter Build() { if (_write is null || _read is null) { throw new InvalidOperationException( $"Polyline surface delegates must be supplied before calling Build. " + - $"Call {nameof(ForPolyline)} first."); + $"Call {nameof(WithReaderWriter)} first."); } - return new PolylineFormatter(_rules.ToArray(), _create, _write, _read); + return new PolylineFormatter(_rules.ToArray(), _create, _write, _read); } } diff --git a/src/PolylineAlgorithm/Internal/CoordinateDelta.cs b/src/PolylineAlgorithm/Internal/CoordinateDelta.cs deleted file mode 100644 index ae217c57..00000000 --- a/src/PolylineAlgorithm/Internal/CoordinateDelta.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.Internal; - -using System.Diagnostics; -using System.Runtime.InteropServices; - -/// -/// Represents the difference (delta) in latitude and longitude between consecutive geographic coordinates. -/// -/// -/// This struct computes and stores the change in coordinate values as integer deltas between successive coordinates. -/// -[DebuggerDisplay("{ToString(),nq}")] -[StructLayout(LayoutKind.Auto)] -internal struct CoordinateDelta { - private (int Latitude, int Longitude) _current; - - /// - /// Initializes a new instance of the struct with the default latitude and longitude deltas. - /// - public CoordinateDelta() { - _current = (default, default); - } - - /// - /// Gets the current delta in latitude between the most recent and previous coordinate. - /// - public int Latitude { get; private set; } - - /// - /// Gets the current delta in longitude between the most recent and previous coordinate. - /// - public int Longitude { get; private set; } - - /// - /// Updates the delta values based on the next latitude and longitude, and sets the current coordinate as next delta baseline. - /// - /// The next latitude value. - /// The next longitude value. - public void Next(int latitude, int longitude) { - Latitude = Delta(_current.Latitude, latitude); - Longitude = Delta(_current.Longitude, longitude); - - _current.Latitude = latitude; - _current.Longitude = longitude; - } - - /// - /// Calculates the delta between two coordinate values. - /// - /// - /// This method computes the difference between two integer coordinate values, handling cases where the values may be positive or negative. - /// - /// The previous coordinate value. - /// The next coordinate value. - /// The computed delta between and . - private static int Delta(int initial, int next) => next - initial; - - /// - /// Returns a string representation of the current coordinate delta. - /// - /// - /// A string in the format { Coordinate: { Latitude: [int], Longitude: [int] }, Delta: { Latitude: [int], Longitude: [int] } } representing the current coordinate and deltas to previous coordinate. - /// - public override readonly string ToString() => - $"{{ Coordinate: {{ Latitude: {_current.Latitude}, Longitude: {_current.Longitude} }}, " + - $"Delta: {{ Latitude: {Latitude}, Longitude: {Longitude} }} }}"; -} \ No newline at end of file diff --git a/src/PolylineAlgorithm/Internal/FormatterRule.cs b/src/PolylineAlgorithm/Internal/FormatterRule.cs index 04ae1f1b..6862e9e6 100644 --- a/src/PolylineAlgorithm/Internal/FormatterRule.cs +++ b/src/PolylineAlgorithm/Internal/FormatterRule.cs @@ -8,7 +8,7 @@ namespace PolylineAlgorithm.Internal; using System; /// -/// Represents a single column rule baked into a . +/// Represents a single column rule baked into a . /// Stores the pre-calculated factor and an optional baseline alongside the user-supplied value selector. /// /// The source object type from which the column value is extracted. diff --git a/src/PolylineAlgorithm/PolylineDecoder.cs b/src/PolylineAlgorithm/PolylineDecoder.cs index 5303ffaa..7d9a3193 100644 --- a/src/PolylineAlgorithm/PolylineDecoder.cs +++ b/src/PolylineAlgorithm/PolylineDecoder.cs @@ -18,119 +18,52 @@ namespace PolylineAlgorithm; /// Decodes encoded polyline representations into sequences of geographic coordinates. /// /// The type that represents the encoded polyline input. -/// The type that represents a decoded geographic coordinate. +/// The type that represents a decoded geographic coordinate. /// -/// Pass a that carries a -/// to the constructor. The formatter handles +/// Pass a that carries a +/// to the constructor. The formatter handles /// all type-specific concerns; no subclassing is required. /// -public class PolylineDecoder : IPolylineDecoder, IChunkedPolylineDecoder { - private readonly IPolylineFormatter _formatter; - private readonly ILogger> _logger; +public class PolylineDecoder : IPolylineDecoder { + private readonly IPolylineFormatter _formatter; + private readonly ILogger> _logger; /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// /// - /// A that carries the formatter and settings. + /// A that carries the formatter and settings. /// Must not be . /// /// /// Thrown when is . /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public PolylineDecoder(PolylineOptions options) { + public PolylineDecoder(PolylineOptions options) { if (options is null) { ExceptionGuard.ThrowArgumentNull(nameof(options)); } _formatter = options.Formatter; - _logger = options.LoggerFactory.CreateLogger>(); + _logger = options.LoggerFactory.CreateLogger>(); } /// /// Decodes an encoded into a sequence of - /// instances. - /// - /// The encoded polyline to decode. Must not be . - /// A token that can be used to cancel the operation. - /// - /// An of representing the decoded - /// coordinates in the original order. - /// - /// - /// Thrown when is . - /// - /// - /// Thrown when the polyline format is invalid or malformed. - /// - /// - /// Thrown when is canceled during decoding. - /// - public IEnumerable Decode(TPolyline polyline, CancellationToken cancellationToken = default) { - const string OperationName = nameof(Decode); - - _logger.LogOperationStartedDebug(OperationName); - - if (polyline is null) { - _logger.LogNullArgumentWarning(nameof(polyline)); - ExceptionGuard.ThrowArgumentNull(nameof(polyline)); - } - - ReadOnlyMemory sequence = _formatter.Read(polyline); - - if (sequence.Length < Defaults.Polyline.Block.Length.Min) { - _logger.LogOperationFailedDebug(OperationName); - _logger.LogPolylineCannotBeShorterThanWarning(sequence.Length, Defaults.Polyline.Block.Length.Min); - ExceptionGuard.ThrowInvalidPolylineLength(sequence.Length, Defaults.Polyline.Block.Length.Min); - } - - try { - PolylineEncoding.ValidateFormat(sequence.Span); - } catch (ArgumentException ex) { - _logger.LogInvalidPolylineFormatWarning(ex); - throw; - } - - int width = _formatter.Width; - long[] accumulated = new long[width]; - int position = 0; - - try { - while (position < sequence.Length) { - cancellationToken.ThrowIfCancellationRequested(); - - for (int j = 0; j < width; j++) { - if (!PolylineEncoding.TryReadValue(ref accumulated[j], sequence, ref position)) { - _logger.LogOperationFailedDebug(OperationName); - _logger.LogInvalidPolylineWarning(position); - ExceptionGuard.ThrowInvalidPolylineFormat(position); - } - } - - yield return _formatter.CreateItem(accumulated.AsSpan()); - } - } finally { - _logger.LogOperationFinishedDebug(OperationName); - } - } - - /// - /// Decodes an encoded into a sequence of - /// instances, applying per-call to + /// instances, applying per-call to /// seed the accumulated-delta state. Use this overload to decode polylines that were produced by /// chunked encoding. /// /// The encoded polyline to decode. Must not be . /// /// Per-call options that control the accumulated-delta seed. Pass or an - /// instance with set to + /// instance with set to /// to start from zero (same as calling /// ). /// /// A token that can be used to cancel the operation. /// - /// An of representing the decoded + /// An of representing the decoded /// coordinates. /// /// @@ -142,10 +75,10 @@ public IEnumerable Decode(TPolyline polyline, CancellationToken can /// /// Thrown when is canceled during decoding. /// - public IEnumerable Decode( + public IEnumerable Decode( TPolyline polyline, - PolylineDecodingOptions? options, - CancellationToken cancellationToken) { + PolylineDecodingOptions? options = null, + CancellationToken cancellationToken = default) { const string OperationName = nameof(Decode); _logger.LogOperationStartedDebug(OperationName); @@ -195,7 +128,7 @@ public IEnumerable Decode( } } - private void SeedAccumulated(long[] accumulated, PolylineDecodingOptions? options) { + private void SeedAccumulated(long[] accumulated, PolylineDecodingOptions? options) { if (options is not { HasPrevious: true }) { return; } diff --git a/src/PolylineAlgorithm/PolylineDecodingOptions.cs b/src/PolylineAlgorithm/PolylineDecodingOptions.cs index facf782d..4ba3c7d9 100644 --- a/src/PolylineAlgorithm/PolylineDecodingOptions.cs +++ b/src/PolylineAlgorithm/PolylineDecodingOptions.cs @@ -8,44 +8,43 @@ namespace PolylineAlgorithm; /// /// Per-call options for a chunked decoding operation. /// -/// The coordinate type understood by the formatter. +/// The value type understood by the formatter. /// /// Pass an instance of this class to the chunked -/// overload to control +/// overload to control /// the accumulated-delta seed used at the start of each chunk. When is /// zero-initialisation is used, which is the existing default behaviour. /// -public sealed class PolylineDecodingOptions { - private readonly TCoordinate _previous; +public sealed class PolylineDecodingOptions { /// - /// Initializes a new instance of with no - /// previous coordinate (zero-initialised baseline will be used). + /// Initializes a new instance of with no + /// previous value (zero-initialised baseline will be used). /// public PolylineDecodingOptions() { } /// - /// Initializes a new instance of with the - /// specified previous coordinate used to seed the accumulated-delta state. + /// Initializes a new instance of with the + /// specified previous value used to seed the accumulated-delta state. /// /// - /// The last coordinate of the previous chunk, used to seed the accumulated-delta state. + /// The last value of the previous chunk, used to seed the accumulated-delta state. /// - public PolylineDecodingOptions(TCoordinate previous) { - _previous = previous; + public PolylineDecodingOptions(TValue previous) { + Previous = previous; HasPrevious = true; } /// - /// Gets a value indicating whether a previous coordinate has been supplied to seed the + /// Gets a value indicating whether a previous value has been supplied to seed the /// accumulated-delta state. When zero-initialisation is used, which is /// the existing default. /// public bool HasPrevious { get; } /// - /// Gets the last coordinate of the previous chunk, used to seed the accumulated-delta state. + /// Gets the last value of the previous chunk, used to seed the accumulated-delta state. /// Only meaningful when is . /// - public TCoordinate Previous => _previous; + public TValue Previous { get; } } diff --git a/src/PolylineAlgorithm/PolylineEncoder.cs b/src/PolylineAlgorithm/PolylineEncoder.cs index 4cc83944..8c59d895 100644 --- a/src/PolylineAlgorithm/PolylineEncoder.cs +++ b/src/PolylineAlgorithm/PolylineEncoder.cs @@ -18,41 +18,41 @@ namespace PolylineAlgorithm; /// /// Encodes sequences of geographic coordinates into encoded polyline representations. /// -/// The type that represents a geographic coordinate to encode. +/// The type that represents a geographic coordinate to encode. /// The type that represents the encoded polyline output. /// -/// Pass a that carries a -/// to the constructor. The formatter handles +/// Pass a that carries a +/// to the constructor. The formatter handles /// all type-specific concerns; no subclassing is required. /// -public class PolylineEncoder : IChunkedPolylineEncoder { - private readonly IPolylineFormatter _formatter; - private readonly PolylineOptions _options; - private readonly ILogger> _logger; +public class PolylineEncoder : IPolylineEncoder { + private readonly IPolylineFormatter _formatter; + private readonly PolylineOptions _options; + private readonly ILogger> _logger; /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// /// - /// A that carries the formatter and settings. + /// A that carries the formatter and settings. /// Must not be . /// /// /// Thrown when is . /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] - public PolylineEncoder(PolylineOptions options) { + public PolylineEncoder(PolylineOptions options) { if (options is null) { ExceptionGuard.ThrowArgumentNull(nameof(options)); } _options = options; _formatter = options.Formatter; - _logger = options.LoggerFactory.CreateLogger>(); + _logger = options.LoggerFactory.CreateLogger>(); } /// - /// Encodes a collection of instances into an encoded + /// Encodes a collection of instances into an encoded /// . /// /// The collection of coordinates to encode. @@ -69,7 +69,7 @@ public PolylineEncoder(PolylineOptions options) { /// /// Thrown when is canceled. /// - public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) { + public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) { const string OperationName = nameof(Encode); _logger.LogOperationStartedDebug(OperationName); @@ -130,7 +130,7 @@ public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken } /// - /// Encodes a collection of instances into an encoded + /// Encodes a collection of instances into an encoded /// , applying per-call to control the /// delta baseline. Use this overload to encode large sequences in independent chunks that can be /// concatenated into a single valid polyline. @@ -138,9 +138,9 @@ public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken /// The collection of coordinates to encode. /// /// Per-call options that control the starting delta baseline. Pass or an - /// instance with set to + /// instance with set to /// to use the formatter's default baseline (same as calling - /// ). + /// ). /// /// A token that can be used to cancel the operation. /// @@ -155,10 +155,7 @@ public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken /// /// Thrown when is canceled. /// - public TPolyline Encode( - ReadOnlySpan coordinates, - PolylineEncodingOptions? options, - CancellationToken cancellationToken) { + public TPolyline Encode(ReadOnlySpan coordinates, PolylineEncodingOptions? options, CancellationToken cancellationToken) { const string OperationName = nameof(Encode); _logger.LogOperationStartedDebug(OperationName); @@ -217,7 +214,7 @@ public TPolyline Encode( } } - private void SeedPrevious(long[] previous, PolylineEncodingOptions? options) { + private void SeedPrevious(long[] previous, PolylineEncodingOptions? options) { int width = _formatter.Width; if (options is { HasPrevious: true }) { diff --git a/src/PolylineAlgorithm/PolylineEncodingOptions.cs b/src/PolylineAlgorithm/PolylineEncodingOptions.cs index d9cc299f..ef3f9dca 100644 --- a/src/PolylineAlgorithm/PolylineEncodingOptions.cs +++ b/src/PolylineAlgorithm/PolylineEncodingOptions.cs @@ -8,31 +8,31 @@ namespace PolylineAlgorithm; /// /// Per-call options for a chunked encoding operation. /// -/// The coordinate type understood by the formatter. +/// The coordinate type understood by the formatter. /// /// Pass an instance of this class to the chunked -/// overload to control +/// overload to control /// the delta baseline used at the start of each chunk. When is /// the formatter's built-in baseline (or zero) is used, which is equivalent /// to the existing default behaviour. /// -public sealed class PolylineEncodingOptions { - private readonly TCoordinate _previous; +public sealed class PolylineEncodingOptions { + private readonly TValue _previous; /// - /// Initializes a new instance of with no + /// Initializes a new instance of with no /// previous coordinate (formatter default baseline will be used). /// public PolylineEncodingOptions() { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified previous coordinate used to seed the delta baseline. /// /// /// The last coordinate of the previous chunk, used to seed the delta baseline. /// - public PolylineEncodingOptions(TCoordinate previous) { + public PolylineEncodingOptions(TValue previous) { _previous = previous; HasPrevious = true; } @@ -49,5 +49,5 @@ public PolylineEncodingOptions(TCoordinate previous) { /// Gets the last coordinate of the previous chunk, used to seed the delta baseline. /// Only meaningful when is . /// - public TCoordinate Previous => _previous; + public TValue Previous => _previous; } diff --git a/src/PolylineAlgorithm/PolylineFormatter.cs b/src/PolylineAlgorithm/PolylineFormatter.cs index d9c1636c..a183b6ad 100644 --- a/src/PolylineAlgorithm/PolylineFormatter.cs +++ b/src/PolylineAlgorithm/PolylineFormatter.cs @@ -11,28 +11,28 @@ namespace PolylineAlgorithm; using System.Runtime.CompilerServices; /// -/// A sealed, immutable formatter that implements . +/// A sealed, immutable formatter that implements . /// -/// The coordinate or item type. +/// The coordinate or item type. /// The polyline surface type. /// -/// Instances are constructed exclusively through . +/// Instances are constructed exclusively through . /// The modifier allows the JIT to devirtualise and inline calls to the /// interface methods in the encoding/decoding hot loop. /// -public sealed class PolylineFormatter : IPolylineFormatter { - private readonly FormatterRule[] _rules; - private readonly PolylineItemFactory? _create; +public sealed class PolylineFormatter : IPolylineFormatter { + private readonly FormatterRule[] _rules; + private readonly PolylineItemFactory? _create; private readonly Func, TPolyline> _write; private readonly Func> _read; /// /// Initializes a new instance. Intentionally internal — use - /// to create instances. + /// to create instances. /// internal PolylineFormatter( - FormatterRule[] rules, - PolylineItemFactory? create, + FormatterRule[] rules, + PolylineItemFactory? create, Func, TPolyline> write, Func> read) { _rules = rules; @@ -54,7 +54,7 @@ internal PolylineFormatter( /// Thrown when .Length does not equal . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void GetValues(TCoordinate item, Span values) { + public void GetValues(TValue item, Span values) { if (values.Length != Width) { throw new ArgumentException( $"Buffer length {values.Length} does not match the formatter width {Width}.", @@ -79,14 +79,14 @@ public void GetValues(TCoordinate item, Span values) { /// /// /// Thrown when no factory delegate was supplied via - /// . + /// . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TCoordinate CreateItem(ReadOnlySpan values) { + public TValue CreateItem(ReadOnlySpan values) { if (_create is null) { throw new InvalidOperationException( $"Cannot reconstruct an item because no factory was registered. " + - $"Call {nameof(FormatterBuilder)}.{nameof(FormatterBuilder.WithCreate)} before building."); + $"Call {nameof(FormatterBuilder)}.{nameof(FormatterBuilder.WithValueFactory)} before building."); } // Denormalize each accumulated scaled integer back to the original double: diff --git a/src/PolylineAlgorithm/PolylineItemFactory.cs b/src/PolylineAlgorithm/PolylineItemFactory.cs index e2e5e23a..7a694416 100644 --- a/src/PolylineAlgorithm/PolylineItemFactory.cs +++ b/src/PolylineAlgorithm/PolylineItemFactory.cs @@ -16,7 +16,7 @@ namespace PolylineAlgorithm; /// The denormalized values reconstructed by the polyline decoder. Each element corresponds to the /// original value that was encoded, with the precision factor divided out and any /// baseline added back. The span length equals the number of columns defined via -/// . +/// . /// /// A instance reconstructed from . public delegate T PolylineItemFactory(ReadOnlySpan values); diff --git a/src/PolylineAlgorithm/PolylineOptions.cs b/src/PolylineAlgorithm/PolylineOptions.cs index 922f5d19..0bdce089 100644 --- a/src/PolylineAlgorithm/PolylineOptions.cs +++ b/src/PolylineAlgorithm/PolylineOptions.cs @@ -13,16 +13,16 @@ namespace PolylineAlgorithm; /// /// Provides unified configuration for a formatter-driven encoding or decoding operation. /// -/// The coordinate or item type understood by the formatter. +/// The coordinate or item type understood by the formatter. /// The polyline surface type understood by the formatter. /// -/// Supply an and optional settings, -/// then pass this instance to and/or -/// . +/// Supply an and optional settings, +/// then pass this instance to and/or +/// . /// -public sealed class PolylineOptions { +public sealed class PolylineOptions { /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// /// /// The unified formatter that handles all type-specific concerns: value extraction, item @@ -39,7 +39,7 @@ public sealed class PolylineOptions { /// Thrown when is . /// public PolylineOptions( - IPolylineFormatter formatter, + IPolylineFormatter formatter, int stackAllocLimit = 512, ILoggerFactory? loggerFactory = null) { if (formatter is null) { @@ -55,7 +55,7 @@ public PolylineOptions( /// Gets the unified formatter that handles value extraction, item reconstruction, and polyline /// surface conversion. /// - public IPolylineFormatter Formatter { get; } + public IPolylineFormatter Formatter { get; } /// /// Gets the maximum buffer size (in characters) that may be allocated on the stack for encoding. diff --git a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt index 6c7fd426..e69de29b 100644 --- a/src/PolylineAlgorithm/PublicAPI.Unshipped.txt +++ b/src/PolylineAlgorithm/PublicAPI.Unshipped.txt @@ -1,77 +0,0 @@ -#nullable enable -PolylineAlgorithm.Abstraction.IPolylineDecoder -PolylineAlgorithm.Abstraction.IPolylineDecoder.Decode(TPolyline polyline, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable! -PolylineAlgorithm.Abstraction.IPolylineEncoder -PolylineAlgorithm.Abstraction.IPolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline -PolylineAlgorithm.Abstraction.IPolylineFormatter -PolylineAlgorithm.Abstraction.IPolylineFormatter.CreateItem(System.ReadOnlySpan values) -> TCoordinate -PolylineAlgorithm.Abstraction.IPolylineFormatter.GetBaseline(int index) -> long -PolylineAlgorithm.Abstraction.IPolylineFormatter.GetValues(TCoordinate item, System.Span values) -> void -PolylineAlgorithm.Abstraction.IPolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory -PolylineAlgorithm.Abstraction.IPolylineFormatter.Width.get -> int -PolylineAlgorithm.Abstraction.IPolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline -PolylineAlgorithm.Extensions.PolylineDecoderExtensions -PolylineAlgorithm.Extensions.PolylineEncoderExtensions -PolylineAlgorithm.FormatterBuilder -PolylineAlgorithm.FormatterBuilder.AddValue(string! name, System.Func! selector, uint precision = 5) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.Build() -> PolylineAlgorithm.PolylineFormatter! -PolylineAlgorithm.FormatterBuilder.ForPolyline(System.Func, TPolyline>! write, System.Func>! read) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.SetBaseline(long baseline) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.FormatterBuilder.WithCreate(PolylineAlgorithm.PolylineItemFactory! create) -> PolylineAlgorithm.FormatterBuilder! -PolylineAlgorithm.InvalidPolylineException -PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException() -> void -PolylineAlgorithm.InvalidPolylineException.InvalidPolylineException(string! message, System.Exception! innerException) -> void -PolylineAlgorithm.PolylineDecoder -PolylineAlgorithm.PolylineDecoder.Decode(TPolyline polyline, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable! -PolylineAlgorithm.PolylineDecoder.PolylineDecoder(PolylineAlgorithm.PolylineOptions! options) -> void -PolylineAlgorithm.PolylineEncoder -PolylineAlgorithm.PolylineEncoder.Encode(System.ReadOnlySpan coordinates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TPolyline -PolylineAlgorithm.PolylineEncoder.PolylineEncoder(PolylineAlgorithm.PolylineOptions! options) -> void -PolylineAlgorithm.PolylineEncoding -PolylineAlgorithm.PolylineFormatter -PolylineAlgorithm.PolylineFormatter.CreateItem(System.ReadOnlySpan values) -> TCoordinate -PolylineAlgorithm.PolylineFormatter.GetBaseline(int index) -> long -PolylineAlgorithm.PolylineFormatter.GetValues(TCoordinate item, System.Span values) -> void -PolylineAlgorithm.PolylineFormatter.Read(TPolyline polyline) -> System.ReadOnlyMemory -PolylineAlgorithm.PolylineFormatter.Width.get -> int -PolylineAlgorithm.PolylineFormatter.Write(System.ReadOnlyMemory encoded) -> TPolyline -PolylineAlgorithm.PolylineItemFactory -PolylineAlgorithm.PolylineOptions -PolylineAlgorithm.PolylineOptions.Formatter.get -> PolylineAlgorithm.Abstraction.IPolylineFormatter! -PolylineAlgorithm.PolylineOptions.LoggerFactory.get -> Microsoft.Extensions.Logging.ILoggerFactory! -PolylineAlgorithm.PolylineOptions.PolylineOptions(PolylineAlgorithm.Abstraction.IPolylineFormatter! formatter, int stackAllocLimit = 512, Microsoft.Extensions.Logging.ILoggerFactory? loggerFactory = null) -> void -PolylineAlgorithm.PolylineOptions.StackAllocLimit.get -> int -static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder, TValue>! decoder, string! polyline) -> System.Collections.Generic.IEnumerable! -static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder! decoder, System.ReadOnlyMemory polyline) -> System.Collections.Generic.IEnumerable! -static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IPolylineDecoder! decoder, char[]! polyline) -> System.Collections.Generic.IEnumerable! -static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IPolylineEncoder! encoder, System.Collections.Generic.List! coordinates) -> TPolyline -static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IPolylineEncoder! encoder, TCoordinate[]! coordinates) -> TPolyline -static PolylineAlgorithm.FormatterBuilder.Create() -> PolylineAlgorithm.FormatterBuilder! -static PolylineAlgorithm.PolylineEncoding.Denormalize(long value, uint precision = 5) -> double -static PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(long delta) -> int -static PolylineAlgorithm.PolylineEncoding.Normalize(double value, uint precision = 5) -> long -static PolylineAlgorithm.PolylineEncoding.TryReadValue(ref long delta, System.ReadOnlyMemory buffer, ref int position) -> bool -static PolylineAlgorithm.PolylineEncoding.TryWriteValue(long delta, System.Span buffer, ref int position) -> bool -static PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan polyline) -> void -static PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan polyline) -> void -static PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan polyline) -> void -virtual PolylineAlgorithm.PolylineItemFactory.Invoke(System.ReadOnlySpan values) -> T -PolylineAlgorithm.Abstraction.IChunkedPolylineDecoder -PolylineAlgorithm.Abstraction.IChunkedPolylineDecoder.Decode(TPolyline polyline, PolylineAlgorithm.PolylineDecodingOptions? options, System.Threading.CancellationToken cancellationToken) -> System.Collections.Generic.IEnumerable! -PolylineAlgorithm.Abstraction.IChunkedPolylineEncoder -PolylineAlgorithm.Abstraction.IChunkedPolylineEncoder.Encode(System.ReadOnlySpan coordinates, PolylineAlgorithm.PolylineEncodingOptions? options, System.Threading.CancellationToken cancellationToken) -> TPolyline -PolylineAlgorithm.PolylineDecodingOptions -PolylineAlgorithm.PolylineDecodingOptions.HasPrevious.get -> bool -PolylineAlgorithm.PolylineDecodingOptions.PolylineDecodingOptions() -> void -PolylineAlgorithm.PolylineDecodingOptions.PolylineDecodingOptions(TCoordinate previous) -> void -PolylineAlgorithm.PolylineDecodingOptions.Previous.get -> TCoordinate -PolylineAlgorithm.PolylineDecoder.Decode(TPolyline polyline, PolylineAlgorithm.PolylineDecodingOptions? options, System.Threading.CancellationToken cancellationToken) -> System.Collections.Generic.IEnumerable! -PolylineAlgorithm.PolylineEncodingOptions -PolylineAlgorithm.PolylineEncodingOptions.HasPrevious.get -> bool -PolylineAlgorithm.PolylineEncodingOptions.PolylineEncodingOptions() -> void -PolylineAlgorithm.PolylineEncodingOptions.PolylineEncodingOptions(TCoordinate previous) -> void -PolylineAlgorithm.PolylineEncodingOptions.Previous.get -> TCoordinate -PolylineAlgorithm.PolylineEncoder.Encode(System.ReadOnlySpan coordinates, PolylineAlgorithm.PolylineEncodingOptions? options, System.Threading.CancellationToken cancellationToken) -> TPolyline -static PolylineAlgorithm.Extensions.PolylineDecoderExtensions.Decode(this PolylineAlgorithm.Abstraction.IChunkedPolylineDecoder! decoder, string! polyline, PolylineAlgorithm.PolylineDecodingOptions? options) -> System.Collections.Generic.IEnumerable! -static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IChunkedPolylineEncoder! encoder, System.Collections.Generic.List! coordinates, PolylineAlgorithm.PolylineEncodingOptions? options) -> TPolyline -static PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode(this PolylineAlgorithm.Abstraction.IChunkedPolylineEncoder! encoder, TCoordinate[]! coordinates, PolylineAlgorithm.PolylineEncodingOptions? options) -> TPolyline diff --git a/src/PolylineAlgorithm/README.md b/src/PolylineAlgorithm/README.md index 3a8e98dd..03dd3cae 100644 --- a/src/PolylineAlgorithm/README.md +++ b/src/PolylineAlgorithm/README.md @@ -1,4 +1,4 @@ -# PolylineAlgorithm for .NET +# PolylineAlgorithm for .NET [![NuGet](https://img.shields.io/nuget/v/PolylineAlgorithm)](https://www.nuget.org/packages/PolylineAlgorithm) @@ -7,9 +7,9 @@ Google's Encoded Polyline Algorithm compresses sequences of geographic coordinat ## Features - Google-compliant polyline encoding/decoding for geographic coordinates -- Fluent `FormatterBuilder` for configuring custom coordinate and polyline types -- Immutable, sealed `PolylineFormatter` built via `FormatterBuilder` -- `PolylineEncoder` and `PolylineDecoder` — configurable via `PolylineOptions` +- Fluent `FormatterBuilder` for configuring custom coordinate and polyline types +- Immutable, sealed `PolylineFormatter` built via `FormatterBuilder` +- `PolylineEncoder` and `PolylineDecoder` — configurable via `PolylineOptions` - Extension methods for encoding from `List` and arrays (`PolylineEncoderExtensions`) - Robust input validation and descriptive exceptions - Configurable stack-alloc buffer size and logging via `PolylineOptions` @@ -106,7 +106,7 @@ Use static methods on `PolylineEncoding` for low-level normalization, validation - **What .NET versions are supported?** Any environment supporting `netstandard2.1` - **How do I customize encoder/decoder options?** - Create a `PolylineOptions` with your `PolylineFormatter`, optional `stackAllocLimit`, and optional `ILoggerFactory`, then pass it to the `PolylineEncoder` or `PolylineDecoder` constructor. + Create a `PolylineOptions` with your `PolylineFormatter`, optional `stackAllocLimit`, and optional `ILoggerFactory`, then pass it to the `PolylineEncoder` or `PolylineDecoder` constructor. - **Where can I get help?** [GitHub issues](https://github.com/petesramek/polyline-algorithm-csharp/issues) diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs index f833e9b2..57c56b28 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineDecoderTests.cs @@ -11,7 +11,7 @@ namespace PolylineAlgorithm.Tests.Abstraction; using System.Threading; /// -/// Tests for . +/// Tests for . /// [TestClass] public sealed class AbstractPolylineDecoderTests { @@ -27,8 +27,8 @@ public sealed class AbstractPolylineDecoderTests { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", static c => c.Lat) .AddValue("lon", static c => c.Lon) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(_write, _read) + .WithValueFactory(static v => (v[0], v[1])) + .WithReaderWriter(_write, _read) .Build(); return new PolylineDecoder( @@ -143,7 +143,7 @@ public void Decode_With_Pre_Cancelled_Token_Throws_OperationCanceledException() // Act & Assert Assert.ThrowsExactly( - () => decoder.Decode(polyline, cts.Token).ToList()); + () => decoder.Decode(polyline, cancellationToken: cts.Token).ToList()); } // ------------------------------------------------------------------ @@ -161,7 +161,7 @@ public void Decode_Without_WithCreate_Throws_InvalidOperationException() { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", c => c.Lat) .AddValue("lon", c => c.Lon) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); PolylineDecoder decoder = @@ -183,7 +183,7 @@ public void Decode_Without_WithCreate_Throws_InvalidOperationException() { /// overload. /// [TestMethod] - public void Decode_Chunked_With_Null_Options_Produces_Same_Result_As_Standard() { + public void Decode_With_Null_Options_Produces_Same_Result_As_Standard() { // Arrange PolylineDecoder decoder = CreateDecoder(); string polyline = StaticValueProvider.Valid.GetPolyline(); @@ -205,14 +205,14 @@ public void Decode_Chunked_With_Null_Options_Produces_Same_Result_As_Standard() /// correctly, producing a result different from standard decoding of the same polyline. /// [TestMethod] - public void Decode_Chunked_With_Previous_Seeds_Accumulated_State() { + public void Decode_With_Previous_Seeds_Accumulated_State() { // Arrange — build a chunked polyline where chunk B is relative to the last of chunk A PolylineFormatter<(double Lat, double Lon), string> formatter = FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", c => c.Lat) .AddValue("lon", c => c.Lon) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(_write, _read) + .WithValueFactory(static v => (v[0], v[1])) + .WithReaderWriter(_write, _read) .Build(); PolylineOptions<(double Lat, double Lon), string> options = new(formatter); @@ -244,14 +244,14 @@ public void Decode_Chunked_With_Previous_Seeds_Accumulated_State() { /// the original sequence. /// [TestMethod] - public void Decode_Chunked_RoundTrip_Reproduces_Full_Sequence() { + public void Decode_RoundTrip_Reproduces_Full_Sequence() { // Arrange PolylineFormatter<(double Lat, double Lon), string> formatter = FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", c => c.Lat) .AddValue("lon", c => c.Lon) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(_write, _read) + .WithValueFactory(static v => (v[0], v[1])) + .WithReaderWriter(_write, _read) .Build(); PolylineOptions<(double Lat, double Lon), string> options = new(formatter); @@ -291,36 +291,4 @@ public void Decode_Chunked_RoundTrip_Reproduces_Full_Sequence() { Assert.AreEqual(all[i].Lon, combined[i].Lon, 1e-5); } } - - /// - /// Tests that a null polyline throws when using the - /// chunked overload. - /// - [TestMethod] - public void Decode_Chunked_With_Null_Polyline_Throws_ArgumentNullException() { - // Arrange - PolylineDecoder decoder = CreateDecoder(); - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => decoder.Decode(null!, null, CancellationToken.None).ToList()); - Assert.AreEqual("polyline", ex.ParamName); - } - - /// - /// Tests that a pre-cancelled token throws in the - /// chunked overload. - /// - [TestMethod] - public void Decode_Chunked_With_Pre_Cancelled_Token_Throws_OperationCanceledException() { - // Arrange - PolylineDecoder decoder = CreateDecoder(); - string polyline = StaticValueProvider.Valid.GetPolyline(); - using CancellationTokenSource cts = new(); - cts.Cancel(); - - // Act & Assert - Assert.ThrowsExactly( - () => decoder.Decode(polyline, null, cts.Token).ToList()); - } } diff --git a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs index 9875ebe6..f026df90 100644 --- a/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs +++ b/tests/PolylineAlgorithm.Tests/Abstraction/AbstractPolylineEncoderTests.cs @@ -10,7 +10,7 @@ namespace PolylineAlgorithm.Tests.Abstraction; using System.Threading; /// -/// Tests for . +/// Tests for . /// [TestClass] public sealed class AbstractPolylineEncoderTests { @@ -26,7 +26,7 @@ public sealed class AbstractPolylineEncoderTests { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", static c => c.Lat) .AddValue("lon", static c => c.Lon) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); return new PolylineEncoder<(double Lat, double Lon), string>( @@ -160,14 +160,14 @@ public void Encode_With_Baseline_Produces_Different_First_Delta() { FormatterBuilder<(double, double), string>.Create() .AddValue("lat", c => c.Item1).SetBaseline(100000L) // scaled 1.0 at precision 5 .AddValue("lon", c => c.Item2) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); PolylineFormatter<(double, double), string> formatterNoBaseline = FormatterBuilder<(double, double), string>.Create() .AddValue("lat", c => c.Item1) .AddValue("lon", c => c.Item2) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); PolylineEncoder<(double, double), string> encoderWithBaseline = @@ -200,8 +200,8 @@ public void Encode_RoundTrip_Produces_Original_Coordinates() { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", c => c.Lat) .AddValue("lon", c => c.Lon) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(_write, _read) + .WithValueFactory(static v => (v[0], v[1])) + .WithReaderWriter(_write, _read) .Build(); PolylineOptions<(double Lat, double Lon), string> options = new(formatter); @@ -277,8 +277,8 @@ public void Encode_Chunked_Concatenated_Decodes_To_Full_Sequence() { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("lat", c => c.Lat) .AddValue("lon", c => c.Lon) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(_write, _read) + .WithValueFactory(static v => (v[0], v[1])) + .WithReaderWriter(_write, _read) .Build(); PolylineOptions<(double Lat, double Lon), string> options = new(formatter); diff --git a/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs deleted file mode 100644 index 3a847760..00000000 --- a/tests/PolylineAlgorithm.Tests/Extensions/PolylineDecoderExtensionsTests.cs +++ /dev/null @@ -1,189 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.Tests.Extensions; - -using PolylineAlgorithm; -using PolylineAlgorithm.Abstraction; -using PolylineAlgorithm.Extensions; -using PolylineAlgorithm.Utility; -using System; -using System.Collections.Generic; - -/// -/// Tests for . -/// -[TestClass] -public sealed class PolylineDecoderExtensionsTests { - private static PolylineDecoder CreateStringDecoder() { - PolylineFormatter<(double Latitude, double Longitude), string> formatter = - FormatterBuilder<(double Latitude, double Longitude), string>.Create() - .AddValue("lat", static c => c.Latitude) - .AddValue("lon", static c => c.Longitude) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) - .Build(); - - return new PolylineDecoder( - new PolylineOptions<(double Latitude, double Longitude), string>(formatter)); - } - - private static PolylineDecoder, (double Latitude, double Longitude)> CreateMemoryDecoder() { - PolylineFormatter<(double Latitude, double Longitude), ReadOnlyMemory> formatter = - FormatterBuilder<(double Latitude, double Longitude), ReadOnlyMemory>.Create() - .AddValue("lat", static c => c.Latitude) - .AddValue("lon", static c => c.Longitude) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(static m => m, static m => m) - .Build(); - - return new PolylineDecoder, (double Latitude, double Longitude)>( - new PolylineOptions<(double Latitude, double Longitude), ReadOnlyMemory>(formatter)); - } - - // ----- Decode(char[]) for IPolylineDecoder ----- - - /// - /// Tests that Decode with a null decoder throws . - /// - [TestMethod] - public void Decode_With_Char_Array_Null_Decoder_Throws_ArgumentNullException() { - // Arrange - IPolylineDecoder? decoder = null; - char[] polyline = StaticValueProvider.Valid.GetPolyline().ToCharArray(); - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => decoder!.Decode(polyline).ToList()); - Assert.AreEqual("decoder", ex.ParamName); - } - - /// - /// Tests that Decode with a null char array throws . - /// - [TestMethod] - public void Decode_With_Char_Array_Null_Polyline_Throws_ArgumentNullException() { - // Arrange - var decoder = CreateStringDecoder(); - char[]? polyline = null; - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => decoder.Decode(polyline!).ToList()); - Assert.AreEqual("polyline", ex.ParamName); - } - - /// - /// Tests that Decode with a valid char array returns expected coordinates. - /// - [TestMethod] - public void Decode_With_Char_Array_Valid_Polyline_Returns_Expected_Coordinates() { - // Arrange - var decoder = CreateStringDecoder(); - char[] polyline = StaticValueProvider.Valid.GetPolyline().ToCharArray(); - (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; - - // Act - (double Latitude, double Longitude)[] result = [.. decoder.Decode(polyline)]; - - // Assert - Assert.AreEqual(expected.Length, result.Length); - for (int i = 0; i < expected.Length; i++) { - Assert.AreEqual(expected[i].Latitude, result[i].Latitude, 1e-5); - Assert.AreEqual(expected[i].Longitude, result[i].Longitude, 1e-5); - } - } - - // ----- Decode(ReadOnlyMemory) for IPolylineDecoder ----- - - /// - /// Tests that Decode with a null decoder throws . - /// - [TestMethod] - public void Decode_With_Memory_Null_Decoder_Throws_ArgumentNullException() { - // Arrange - IPolylineDecoder? decoder = null; - ReadOnlyMemory polyline = StaticValueProvider.Valid.GetPolyline().AsMemory(); - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => decoder!.Decode(polyline).ToList()); - Assert.AreEqual("decoder", ex.ParamName); - } - - /// - /// Tests that Decode with a valid memory returns expected coordinates. - /// - [TestMethod] - public void Decode_With_Memory_Valid_Polyline_Returns_Expected_Coordinates() { - // Arrange - var decoder = CreateStringDecoder(); - ReadOnlyMemory polyline = StaticValueProvider.Valid.GetPolyline().AsMemory(); - (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; - - // Act - (double Latitude, double Longitude)[] result = [.. decoder.Decode(polyline)]; - - // Assert - Assert.AreEqual(expected.Length, result.Length); - for (int i = 0; i < expected.Length; i++) { - Assert.AreEqual(expected[i].Latitude, result[i].Latitude, 1e-5); - Assert.AreEqual(expected[i].Longitude, result[i].Longitude, 1e-5); - } - } - - // ----- Decode(string) for IPolylineDecoder, TValue> ----- - - /// - /// Tests that Decode with a null decoder throws . - /// - [TestMethod] - public void Decode_With_String_Null_Decoder_Throws_ArgumentNullException() { - // Arrange - IPolylineDecoder, (double, double)>? decoder = null; - string polyline = StaticValueProvider.Valid.GetPolyline(); - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => PolylineDecoderExtensions.Decode<(double, double)>(decoder!, polyline).ToList()); - Assert.AreEqual("decoder", ex.ParamName); - } - - /// - /// Tests that Decode with a null string throws . - /// - [TestMethod] - public void Decode_With_String_Null_Polyline_Throws_ArgumentNullException() { - // Arrange - var decoder = CreateMemoryDecoder(); - string? polyline = null; - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => decoder.Decode(polyline!).ToList()); - Assert.AreEqual("polyline", ex.ParamName); - } - - /// - /// Tests that Decode with a valid string returns expected coordinates. - /// - [TestMethod] - public void Decode_With_String_Valid_Polyline_Returns_Expected_Coordinates() { - // Arrange - var decoder = CreateMemoryDecoder(); - string polyline = StaticValueProvider.Valid.GetPolyline(); - (double Latitude, double Longitude)[] expected = [.. StaticValueProvider.Valid.GetCoordinates()]; - - // Act - (double Latitude, double Longitude)[] result = [.. decoder.Decode(polyline)]; - - // Assert - Assert.AreEqual(expected.Length, result.Length); - for (int i = 0; i < expected.Length; i++) { - Assert.AreEqual(expected[i].Latitude, result[i].Latitude, 1e-5); - Assert.AreEqual(expected[i].Longitude, result[i].Longitude, 1e-5); - } - } -} diff --git a/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs b/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs index 21aea09b..c72a1568 100644 --- a/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs +++ b/tests/PolylineAlgorithm.Tests/Extensions/PolylineEncoderExtensionsTests.cs @@ -22,62 +22,13 @@ public sealed class PolylineEncoderExtensionsTests { FormatterBuilder<(double Latitude, double Longitude), string>.Create() .AddValue("lat", static c => c.Latitude) .AddValue("lon", static c => c.Longitude) - .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .WithReaderWriter(static m => new string(m.Span), static s => s.AsMemory()) .Build(); return new PolylineEncoder<(double Latitude, double Longitude), string>( new PolylineOptions<(double Latitude, double Longitude), string>(formatter)); } - // ----- Encode(List) ----- - - /// - /// Tests that Encode with a null encoder throws . - /// - [TestMethod] - public void Encode_With_List_Null_Encoder_Throws_ArgumentNullException() { - // Arrange — use interface type so the extension method is resolved - IPolylineEncoder<(double, double), string>? encoder = null; - List<(double, double)> coordinates = [(0.0, 0.0)]; - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => encoder!.Encode(coordinates)); - Assert.AreEqual("encoder", ex.ParamName); - } - - /// - /// Tests that Encode with a null list throws . - /// - [TestMethod] - public void Encode_With_List_Null_Coordinates_Throws_ArgumentNullException() { - // Arrange - var encoder = CreateTestEncoder(); - List<(double, double)>? coordinates = null; - - // Act & Assert - ArgumentNullException ex = Assert.ThrowsExactly( - () => encoder.Encode(coordinates!)); - Assert.AreEqual("coordinates", ex.ParamName); - } - - /// - /// Tests that Encode with a valid list returns the expected polyline. - /// - [TestMethod] - public void Encode_With_List_Valid_Coordinates_Returns_Expected_Polyline() { - // Arrange - var encoder = CreateTestEncoder(); - List<(double Latitude, double Longitude)> coordinates = [.. StaticValueProvider.Valid.GetCoordinates()]; - string expected = StaticValueProvider.Valid.GetPolyline(); - - // Act - string result = encoder.Encode(coordinates); - - // Assert - Assert.AreEqual(expected, result); - } - // ----- Encode(T[]) ----- /// diff --git a/tests/PolylineAlgorithm.Tests/Internal/CoordinateDeltaTests.cs b/tests/PolylineAlgorithm.Tests/Internal/CoordinateDeltaTests.cs deleted file mode 100644 index 1e915fe2..00000000 --- a/tests/PolylineAlgorithm.Tests/Internal/CoordinateDeltaTests.cs +++ /dev/null @@ -1,136 +0,0 @@ -// -// Copyright © Pete Sramek. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace PolylineAlgorithm.Tests.Internal; - -using PolylineAlgorithm.Internal; -using System.Globalization; - -/// -/// Tests for . -/// -[TestClass] -public sealed class CoordinateDeltaTests { - /// - /// Tests that the default constructor initializes delta values to zero. - /// - [TestMethod] - public void Constructor_Default_Initializes_Latitude_And_Longitude_To_Zero() { - // Act - CoordinateDelta delta = new(); - - // Assert - Assert.AreEqual(0, delta.Latitude); - Assert.AreEqual(0, delta.Longitude); - } - - /// - /// Tests that a single call to Next computes the correct delta from the initial zero state. - /// - [TestMethod] - [DataRow(10, 20, 10, 20)] - [DataRow(-50, -100, -50, -100)] - [DataRow(0, 0, 0, 0)] - [DataRow(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)] - [DataRow(int.MinValue, int.MinValue, int.MinValue, int.MinValue)] - public void Next_Single_Call_From_Zero_Computes_Expected_Delta(int latitude, int longitude, int expectedLatitude, int expectedLongitude) { - // Arrange - CoordinateDelta delta = new(); - - // Act - delta.Next(latitude, longitude); - - // Assert - Assert.AreEqual(expectedLatitude, delta.Latitude); - Assert.AreEqual(expectedLongitude, delta.Longitude); - } - - /// - /// Tests that two consecutive calls to Next compute the delta relative to the previous value. - /// - [TestMethod] - [DataRow(10, 20, 15, 30, 5, 10)] - [DataRow(100, 200, 50, 150, -50, -50)] - [DataRow(42, 84, 42, 84, 0, 0)] - [DataRow(-50, 100, 25, -75, 75, -175)] - public void Next_Sequential_Calls_Compute_Delta_From_Previous_Value( - int firstLatitude, int firstLongitude, - int secondLatitude, int secondLongitude, - int expectedLatitude, int expectedLongitude) { - // Arrange - CoordinateDelta delta = new(); - delta.Next(firstLatitude, firstLongitude); - - // Act - delta.Next(secondLatitude, secondLongitude); - - // Assert - Assert.AreEqual(expectedLatitude, delta.Latitude); - Assert.AreEqual(expectedLongitude, delta.Longitude); - } - - /// - /// Tests that ToString on a default instance returns a string containing expected structural keywords and a zero value. - /// - [TestMethod] - public void ToString_With_Default_Constructor_Returns_Formatted_String_With_Zeros() { - // Arrange - CoordinateDelta delta = new(); - - // Act - string result = delta.ToString(); - - // Assert - Assert.IsNotNull(result); - Assert.IsTrue(result.Contains("Coordinate", StringComparison.Ordinal)); - Assert.IsTrue(result.Contains("Delta", StringComparison.Ordinal)); - Assert.IsTrue(result.Contains("Latitude", StringComparison.Ordinal)); - Assert.IsTrue(result.Contains("Longitude", StringComparison.Ordinal)); - Assert.Contains('0', result); - } - - /// - /// Tests that ToString reflects the delta values computed by Next. - /// - [TestMethod] - [DataRow(42, 84)] - [DataRow(-100, -200)] - [DataRow(int.MaxValue, int.MaxValue)] - [DataRow(int.MinValue, int.MinValue)] - public void ToString_After_Next_Contains_Expected_Values(int latitude, int longitude) { - // Arrange - CoordinateDelta delta = new(); - delta.Next(latitude, longitude); - - // Act - string result = delta.ToString(); - - // Assert - Assert.IsNotNull(result); - Assert.IsTrue(result.Contains(latitude.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal)); - Assert.IsTrue(result.Contains(longitude.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal)); - } - - /// - /// Tests that ToString after multiple Next calls reflects the most recent delta values. - /// - [TestMethod] - public void ToString_After_Multiple_Next_Calls_Returns_Formatted_String_With_Latest_Values() { - // Arrange - CoordinateDelta delta = new(); - delta.Next(10, 20); - delta.Next(30, 50); - - // Act - string result = delta.ToString(); - - // Assert - Assert.IsNotNull(result); - Assert.IsTrue(result.Contains("30", StringComparison.Ordinal)); - Assert.IsTrue(result.Contains("50", StringComparison.Ordinal)); - Assert.IsTrue(result.Contains("20", StringComparison.Ordinal)); - } - -} diff --git a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs index 96d5df52..e9abd8ca 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineEncodingOptionsBuilderTests.cs @@ -9,9 +9,9 @@ namespace PolylineAlgorithm.Tests; using System; /// -/// Tests for , -/// , and -/// write/read/create delegation. +/// Tests for , +/// , and +/// write/read/create delegation. /// [TestClass] public sealed class PolylineEncodingOptionsBuilderTests { @@ -31,7 +31,7 @@ public void ForPolyline_With_Null_Write_Throws_ArgumentNullException() { // Act & Assert ArgumentNullException ex = Assert.ThrowsExactly( - () => builder.ForPolyline(null!, _read)); + () => builder.WithReaderWriter(null!, _read)); Assert.AreEqual("write", ex.ParamName); } @@ -44,7 +44,7 @@ public void ForPolyline_With_Null_Read_Throws_ArgumentNullException() { // Act & Assert ArgumentNullException ex = Assert.ThrowsExactly( - () => builder.ForPolyline(_write, null!)); + () => builder.WithReaderWriter(_write, null!)); Assert.AreEqual("read", ex.ParamName); } @@ -56,7 +56,7 @@ public void ForPolyline_Returns_Same_Builder_For_Method_Chaining() { .AddValue("Value", static v => v); // Act - FormatterBuilder result = builder.ForPolyline(_write, _read); + FormatterBuilder result = builder.WithReaderWriter(_write, _read); // Assert Assert.AreSame(builder, result); @@ -86,7 +86,7 @@ public void WithCreate_With_Null_Factory_Throws_ArgumentNullException() { // Act & Assert ArgumentNullException ex = Assert.ThrowsExactly( - () => builder.WithCreate(null!)); + () => builder.WithValueFactory(null!)); Assert.AreEqual("create", ex.ParamName); } @@ -98,7 +98,7 @@ public void WithCreate_Returns_Same_Builder_For_Method_Chaining() { .AddValue("Value", static v => v); // Act - FormatterBuilder result = builder.WithCreate(static v => v[0]); + FormatterBuilder result = builder.WithValueFactory(static v => v[0]); // Assert Assert.AreSame(builder, result); @@ -115,7 +115,7 @@ public void Write_Delegates_To_Supplied_Write_Function() { ReadOnlyMemory input = "hello".AsMemory(); PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -131,7 +131,7 @@ public void Read_Delegates_To_Supplied_Read_Function() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -151,7 +151,7 @@ public void CreateItem_Without_Factory_Throws_InvalidOperationException() { // Arrange — no WithCreate call PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act & Assert @@ -166,8 +166,8 @@ public void CreateItem_With_Factory_Returns_Expected_Value() { // by the factor (1e5), so the factory receives the denormalized double directly. PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) - .WithCreate(static v => v[0]) - .ForPolyline(_write, _read) + .WithValueFactory(static v => v[0]) + .WithReaderWriter(_write, _read) .Build(); // Act — accumulated value 3850000 → 3850000 / 100000.0 = 38.5 passed to factory @@ -185,8 +185,8 @@ public void CreateItem_With_Multi_Value_Factory_Returns_Expected_Tuple() { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("Lat", static t => t.Lat, precision: 5) .AddValue("Lon", static t => t.Lon, precision: 5) - .WithCreate(static v => (v[0], v[1])) - .ForPolyline(_write, _read) + .WithValueFactory(static v => (v[0], v[1])) + .WithReaderWriter(_write, _read) .Build(); // Act diff --git a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs index cd72b29c..3f5f8650 100644 --- a/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs +++ b/tests/PolylineAlgorithm.Tests/PolylineFormatterTests.cs @@ -11,8 +11,8 @@ namespace PolylineAlgorithm.Tests; using System; /// -/// Tests for and -/// . +/// Tests for and +/// . /// [TestClass] public sealed class PolylineFormatterTests { @@ -113,7 +113,7 @@ public void AddValue_With_Different_Names_Succeeds() { PolylineFormatter<(double X, double Y), string> formatter = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Assert @@ -157,7 +157,7 @@ public void SetBaseline_Applies_Only_To_Last_Added_Rule() { .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .SetBaseline(500L) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Assert — only Y (index 1) has a baseline; X (index 0) returns 0 @@ -171,7 +171,7 @@ public void SetBaseline_Can_Be_Called_On_Each_Rule() { PolylineFormatter<(double X, double Y), string> formatter = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X).SetBaseline(100L) .AddValue("Y", static t => t.Y).SetBaseline(200L) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Assert @@ -197,7 +197,7 @@ public void Build_With_Multiple_Invocations_Returns_Different_Instances() { // Arrange FormatterBuilder<(double X, double Y), string> builder = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X) - .ForPolyline(_write, _read); + .WithReaderWriter(_write, _read); // Act PolylineFormatter<(double X, double Y), string> first = builder.Build(); @@ -218,7 +218,7 @@ public void Width_Equals_Number_Of_Added_Rules() { .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) .AddValue("Z", static t => t.Z) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Assert @@ -230,7 +230,7 @@ public void Width_Is_One_For_Single_Rule() { // Arrange & Act PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Assert @@ -246,7 +246,7 @@ public void GetBaseline_Returns_Zero_When_No_Baseline_Configured() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -262,7 +262,7 @@ public void GetBaseline_Returns_Configured_Baseline() { PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .SetBaseline(42L) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -278,7 +278,7 @@ public void GetBaseline_Returns_Negative_Baseline() { PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) .SetBaseline(-1000L) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -297,7 +297,7 @@ public void GetValues_Scales_Single_Column_By_Factor() { // Arrange — precision 5 → factor = 100000; use a value exact in double arithmetic PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); Span output = stackalloc long[1]; @@ -316,7 +316,7 @@ public void GetValues_Scales_Multiple_Columns_Independently() { FormatterBuilder<(double Lat, double Lon), string>.Create() .AddValue("Lat", static t => t.Lat, precision: 5) .AddValue("Lon", static t => t.Lon, precision: 5) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); Span output = stackalloc long[2]; @@ -335,7 +335,7 @@ public void GetValues_With_Wrong_Buffer_Length_Throws_ArgumentException() { PolylineFormatter<(double X, double Y), string> formatter = FormatterBuilder<(double X, double Y), string>.Create() .AddValue("X", static t => t.X) .AddValue("Y", static t => t.Y) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); long[] tooShort = new long[1]; @@ -351,7 +351,7 @@ public void GetValues_With_Oversized_Buffer_Throws_ArgumentException() { // Arrange — formatter has Width = 1 but buffer has length 3 PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); long[] tooLong = new long[3]; @@ -367,7 +367,7 @@ public void GetValues_With_Zero_Returns_Zero() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); Span output = stackalloc long[1]; @@ -384,7 +384,7 @@ public void GetValues_With_Negative_Value_Returns_Negative_Scaled_Long() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 5) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); Span output = stackalloc long[1]; @@ -401,7 +401,7 @@ public void GetValues_With_Custom_Precision_Scales_Correctly() { // Arrange — precision 3 → factor = 1000 PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v, precision: 3) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); Span output = stackalloc long[1]; @@ -414,7 +414,7 @@ public void GetValues_With_Custom_Precision_Scales_Correctly() { } // --------------------------------------------------------------------------- - // PolylineOptions constructor validation + // PolylineOptions constructor validation // --------------------------------------------------------------------------- [TestMethod] @@ -426,7 +426,7 @@ public void PolylineOptions_With_Null_Formatter_Throws_ArgumentNullException() { } // --------------------------------------------------------------------------- - // PolylineOptions properties + // PolylineOptions properties // --------------------------------------------------------------------------- [TestMethod] @@ -434,7 +434,7 @@ public void PolylineOptions_Stores_Formatter() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -449,7 +449,7 @@ public void PolylineOptions_Default_StackAllocLimit_Is_512() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -464,7 +464,7 @@ public void PolylineOptions_Stores_Custom_StackAllocLimit() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -479,7 +479,7 @@ public void PolylineOptions_Default_LoggerFactory_Is_NullLoggerFactory() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); // Act @@ -494,7 +494,7 @@ public void PolylineOptions_Stores_Custom_LoggerFactory() { // Arrange PolylineFormatter formatter = FormatterBuilder.Create() .AddValue("Value", static v => v) - .ForPolyline(_write, _read) + .WithReaderWriter(_write, _read) .Build(); using ILoggerFactory loggerFactory = LoggerFactory.Create(_ => { }); diff --git a/utilities/PolylineAlgorithm.Utility/RandomValueProvider.cs b/utilities/PolylineAlgorithm.Utility/RandomValueProvider.cs index e17c7852..a5ab9f71 100644 --- a/utilities/PolylineAlgorithm.Utility/RandomValueProvider.cs +++ b/utilities/PolylineAlgorithm.Utility/RandomValueProvider.cs @@ -26,7 +26,7 @@ internal static class RandomValueProvider { FormatterBuilder<(double Latitude, double Longitude), string>.Create() .AddValue("lat", static c => c.Latitude) .AddValue("lon", static c => c.Longitude) - .ForPolyline(static m => new string(m.Span), static s => s.AsMemory()) + .WithReaderWriter(static m => new string(m.Span), static s => s.AsMemory()) .Build(); return new PolylineEncoder<(double Latitude, double Longitude), string>( From d9e247219af562a3bf05ec0876121a4349d02f08 Mon Sep 17 00:00:00 2001 From: Pete Sramek <2333452+petesramek@users.noreply.github.com> Date: Mon, 13 Apr 2026 14:16:08 +0000 Subject: [PATCH 22/22] Updated docs for version 0.0 --- ...gorithm.Abstraction.IPolylineDecoder-2.yml | 99 ++++ ...gorithm.Abstraction.IPolylineEncoder-2.yml | 149 +++++ ...rithm.Abstraction.IPolylineFormatter-2.yml | 204 +++++++ .../0.0/PolylineAlgorithm.Abstraction.yml | 35 ++ ...m.Extensions.PolylineEncoderExtensions.yml | 102 ++++ .../0.0/PolylineAlgorithm.Extensions.yml | 15 + .../PolylineAlgorithm.FormatterBuilder-2.yml | 354 ++++++++++++ ...lineAlgorithm.InvalidPolylineException.yml | 102 ++++ .../PolylineAlgorithm.PolylineDecoder-2.yml | 165 ++++++ ...ineAlgorithm.PolylineDecodingOptions-1.yml | 117 ++++ .../PolylineAlgorithm.PolylineEncoder-2.yml | 210 +++++++ .../PolylineAlgorithm.PolylineEncoding.yml | 527 ++++++++++++++++++ ...ineAlgorithm.PolylineEncodingOptions-1.yml | 121 ++++ .../PolylineAlgorithm.PolylineFormatter-2.yml | 229 ++++++++ ...olylineAlgorithm.PolylineItemFactory-1.yml | 54 ++ .../PolylineAlgorithm.PolylineOptions-2.yml | 158 ++++++ api-reference/0.0/PolylineAlgorithm.yml | 67 +++ api-reference/0.0/toc.yml | 42 ++ 18 files changed, 2750 insertions(+) create mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.Abstraction.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.Extensions.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.FormatterBuilder-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineDecoder-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineDecodingOptions-1.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineEncoder-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions-1.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineFormatter-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml create mode 100644 api-reference/0.0/PolylineAlgorithm.yml create mode 100644 api-reference/0.0/toc.yml diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml new file mode 100644 index 00000000..ce7f47e1 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml @@ -0,0 +1,99 @@ +### YamlMime:ApiPage +title: Interface IPolylineDecoder +body: +- api1: Interface IPolylineDecoder + id: PolylineAlgorithm_Abstraction_IPolylineDecoder_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L22 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineDecoder`2 + commentId: T:PolylineAlgorithm.Abstraction.IPolylineDecoder`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm.Abstraction + url: PolylineAlgorithm.Abstraction.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Defines a contract for decoding an encoded polyline into a sequence of geographic coordinates. +- code: public interface IPolylineDecoder +- h4: Type Parameters +- parameters: + - name: TPolyline + description: >- + The type that represents the encoded polyline input. Common implementations use , + + but custom wrapper types are allowed to carry additional metadata. + - name: TValue + description: >- + The coordinate type returned by the decoder. Typical implementations return a struct or class that + + contains latitude and longitude (for example a LatLng type or a ValueTuple<double,double>). +- h2: Methods +- api3: Decode(TPolyline, PolylineDecodingOptions?, CancellationToken) + id: PolylineAlgorithm_Abstraction_IPolylineDecoder_2_Decode__0_PolylineAlgorithm_PolylineDecodingOptions__1__System_Threading_CancellationToken_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineDecoder.cs#L48 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineDecoder`2.Decode(`0,PolylineAlgorithm.PolylineDecodingOptions{`1},System.Threading.CancellationToken) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineDecoder`2.Decode(`0,PolylineAlgorithm.PolylineDecodingOptions{`1},System.Threading.CancellationToken) +- markdown: >- + Decodes the specified encoded polyline into an ordered sequence of geographic coordinates. + + The sequence preserves the original vertex order encoded by the polyline. +- code: IEnumerable Decode(TPolyline polyline, PolylineDecodingOptions? options = null, CancellationToken cancellationToken = default) +- h4: Parameters +- parameters: + - name: polyline + type: + - TPolyline + description: >- + The TPolyline instance containing the encoded polyline to decode. + + Implementations SHOULD validate the input and may throw + + or for invalid formats. + - name: options + type: + - text: PolylineDecodingOptions + url: PolylineAlgorithm.PolylineDecodingOptions-1.html + - < + - TValue + - '>' + - '?' + optional: true + - name: cancellationToken + type: + - text: CancellationToken + url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken + description: >- + A to observe while decoding. If cancellation is requested, + + implementations SHOULD stop work and throw an . + optional: true +- h4: Returns +- parameters: + - type: + - text: IEnumerable + url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1 + - < + - TValue + - '>' + description: >- + An of TValue representing the decoded + + latitude/longitude pairs (or equivalent coordinates) in the same order they were encoded. +- h4: Remarks +- markdown: >- + Implementations commonly follow the Google Encoded Polyline Algorithm Format, but this interface + + does not mandate a specific encoding. Consumers should rely on a concrete decoder's documentation + + to understand the exact encoding supported. +- h4: Exceptions +- parameters: + - type: + - text: OperationCanceledException + url: https://learn.microsoft.com/dotnet/api/system.operationcanceledexception + description: Thrown when the provided cancellationToken requests cancellation. +languageId: csharp +metadata: + description: Defines a contract for decoding an encoded polyline into a sequence of geographic coordinates. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml new file mode 100644 index 00000000..8951896e --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml @@ -0,0 +1,149 @@ +### YamlMime:ApiPage +title: Interface IPolylineEncoder +body: +- api1: Interface IPolylineEncoder + id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L36 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2 + commentId: T:PolylineAlgorithm.Abstraction.IPolylineEncoder`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm.Abstraction + url: PolylineAlgorithm.Abstraction.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Contract for encoding a sequence of geographic coordinates into an encoded polyline representation. + + Implementations interpret the generic TValue type and produce an encoded + + representation of those coordinates as TPolyline. +- code: public interface IPolylineEncoder +- h4: Type Parameters +- parameters: + - name: TValue + description: >- + The concrete coordinate representation used by the encoder (for example a struct or class containing + + Latitude and Longitude values). Implementations must document the expected shape, + + units (typically decimal degrees), and any required fields for TValue. + + Common shapes: + + - A struct or class with two double properties named Latitude and Longitude. + + - A tuple-like type (for example ValueTuple<double,double>) where the encoder documents + which element represents latitude and longitude. + - name: TPolyline + description: >- + The encoded polyline representation returned by the encoder (for example string, + + ReadOnlyMemory<char>, or a custom wrapper type). Concrete implementations should document + + the chosen representation and any memory / ownership expectations. +- h4: Extension Methods +- list: + - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, TValue[], PolylineEncodingOptions?, CancellationToken) + url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___PolylineAlgorithm_PolylineEncodingOptions___0__System_Threading_CancellationToken_ +- h2: Remarks +- markdown: >- + - This interface is intentionally minimal to allow different encoding strategies (Google encoded polyline, + precision/scale variants, or custom compressed formats) to be expressed behind a common contract. + - Implementations should document: + - Coordinate precision and rounding rules (for example 1e-5 for 5-decimal precision). + - Coordinate ordering and whether altitude or additional dimensions are supported. + - Thread-safety guarantees: whether instances are safe to reuse concurrently or must be instantiated per-call. + - Implementations are encouraged to be memory-efficient; the API accepts a + to avoid forced allocations when callers already have contiguous memory. +- h2: Methods +- api3: Encode(ReadOnlySpan, PolylineEncodingOptions?, CancellationToken) + id: PolylineAlgorithm_Abstraction_IPolylineEncoder_2_Encode_System_ReadOnlySpan__0__PolylineAlgorithm_PolylineEncodingOptions__0__System_Threading_CancellationToken_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs#L76 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},PolylineAlgorithm.PolylineEncodingOptions{`0},System.Threading.CancellationToken) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineEncoder`2.Encode(System.ReadOnlySpan{`0},PolylineAlgorithm.PolylineEncodingOptions{`0},System.Threading.CancellationToken) +- markdown: >- + Encodes a sequence of geographic coordinates into an encoded polyline representation. + + The order of coordinates in coordinates is preserved in the encoded result. +- code: TPolyline Encode(ReadOnlySpan coordinates, PolylineEncodingOptions? options = null, CancellationToken cancellationToken = default) +- h4: Parameters +- parameters: + - name: coordinates + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - TValue + - '>' + description: >- + The collection of TValue instances to encode into a polyline. + + The span may be empty; implementations should return an appropriate empty encoded representation + + (for example an empty string or an empty memory slice) rather than null. + - name: options + type: + - text: PolylineEncodingOptions + url: PolylineAlgorithm.PolylineEncodingOptions-1.html + - < + - TValue + - '>' + - '?' + optional: true + - name: cancellationToken + type: + - text: CancellationToken + url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken + description: >- + A that can be used to cancel the encoding operation. + + Implementations should observe this token and throw + + when cancellation is requested. For fast, in-memory encoders cancellation may be best-effort. + optional: true +- h4: Returns +- parameters: + - type: + - TPolyline + description: >- + A TPolyline containing the encoded polyline that represents the input coordinates. + + The exact format and any delimiting/terminating characters are implementation-specific and must be + + documented by concrete encoder types. +- h4: Examples +- markdown: >- +
// Example pseudocode for typical usage with a string-based encoder:
+
+    var coords = new[] {
+        new Coordinate { Latitude = 47.6219, Longitude = -122.3503 },
+        new Coordinate { Latitude = 47.6220, Longitude = -122.3504 }
+    };
+
+    IPolylineEncoder<Coordinate,string> encoder = new GoogleEncodedPolylineEncoder();
+
+    string encoded = encoder.Encode(coords, CancellationToken.None);
+- h4: Remarks +- markdown: >- + - Implementations should validate input as appropriate and document any preconditions (for example + if coordinates must be within [-90,90] latitude and [-180,180] longitude). + - For large input sequences, implementations may provide streaming or incremental encoders; those + variants can still implement this interface by materializing the final encoded result. +- h4: Exceptions +- parameters: + - type: + - text: OperationCanceledException + url: https://learn.microsoft.com/dotnet/api/system.operationcanceledexception + description: Thrown if the operation is canceled via cancellationToken. +languageId: csharp +metadata: + description: >- + Contract for encoding a sequence of geographic coordinates into an encoded polyline representation. + + Implementations interpret the generic TValue type and produce an encoded + + representation of those coordinates as TPolyline. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-2.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-2.yml new file mode 100644 index 00000000..5de57abd --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.IPolylineFormatter-2.yml @@ -0,0 +1,204 @@ +### YamlMime:ApiPage +title: Interface IPolylineFormatter +body: +- api1: Interface IPolylineFormatter + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L23 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`2 + commentId: T:PolylineAlgorithm.Abstraction.IPolylineFormatter`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm.Abstraction + url: PolylineAlgorithm.Abstraction.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Defines how to extract and scale values from a TValue for encoding, + + reconstruct a TValue from scaled values for decoding, + + produce a TPolyline from an encoded character buffer, and extract that buffer + + back from a TPolyline. +- code: public interface IPolylineFormatter +- h4: Type Parameters +- parameters: + - name: TValue + description: The coordinate or item type. For example a struct with Latitude/Longitude. + - name: TPolyline + description: >- + The polyline surface type. For example or + of . +- h2: Remarks +- markdown: >- + Use to build a + + that implements this interface. +- h2: Properties +- api3: Width + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_2_Width + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L29 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`2.Width + commentId: P:PolylineAlgorithm.Abstraction.IPolylineFormatter`2.Width +- markdown: >- + Gets the number of values (columns) per encoded item. + + This is the required length of the passed to + + and the length of the span received in . +- code: int Width { get; } +- h4: Property Value +- parameters: + - type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 +- h2: Methods +- api3: CreateItem(ReadOnlySpan) + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_2_CreateItem_System_ReadOnlySpan_System_Int64__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L76 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`2.CreateItem(System.ReadOnlySpan{System.Int64}) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`2.CreateItem(System.ReadOnlySpan{System.Int64}) +- markdown: >- + Reconstructs a TValue from the given accumulated scaled integer values. + + Called once per decoded item in the decoding loop. Implementations are responsible for + + denormalizing the raw scaled integers (e.g. dividing by the precision factor and adding back + + any baseline) before constructing the item. +- code: TValue CreateItem(ReadOnlySpan values) +- h4: Parameters +- parameters: + - name: values + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: >- + The raw accumulated scaled integer values decoded from the polyline. Each element corresponds to + + the same column position as in . These are the direct output of the + + delta-accumulation loop in the decoder before any denormalization is applied. +- h4: Returns +- parameters: + - type: + - TValue + description: A TValue reconstructed from values. +- api3: GetBaseline(int) + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_2_GetBaseline_System_Int32_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L38 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`2.GetBaseline(System.Int32) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`2.GetBaseline(System.Int32) +- markdown: >- + Returns the baseline for the column at index, or 0 if none is configured. + + The encoder uses this as the starting point for the first item's delta computation: the initial + + delta for the column is scaled_first_value − baseline rather than scaled_first_value. +- code: long GetBaseline(int index) +- h4: Parameters +- parameters: + - name: index + type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + description: The zero-based column index. Must be in the range [0, ). +- h4: Returns +- parameters: + - type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: The baseline value, or 0 when no baseline has been defined for the column. +- api3: GetValues(TValue, Span) + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_2_GetValues__0_System_Span_System_Int64__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L48 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`2.GetValues(`0,System.Span{System.Int64}) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`2.GetValues(`0,System.Span{System.Int64}) +- markdown: >- + Extracts and scales all column values from item into the values span. + + Called once per item in the encoding loop. +- code: void GetValues(TValue item, Span values) +- h4: Parameters +- parameters: + - name: item + type: + - TValue + description: The source item from which column values are extracted. + - name: values + type: + - text: Span + url: https://learn.microsoft.com/dotnet/api/system.span-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: Output buffer that receives the scaled integer values. Its length must equal . +- api3: Read(TPolyline) + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_2_Read__1_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L62 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`2.Read(`1) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`2.Read(`1) +- markdown: Extracts the character buffer from a TPolyline for the decoder to read. +- code: ReadOnlyMemory Read(TPolyline polyline) +- h4: Parameters +- parameters: + - name: polyline + type: + - TPolyline + description: The polyline to read from. +- h4: Returns +- parameters: + - type: + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: A of representing the encoded characters. +- api3: Write(ReadOnlyMemory) + id: PolylineAlgorithm_Abstraction_IPolylineFormatter_2_Write_System_ReadOnlyMemory_System_Char__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Abstraction/IPolylineFormatter.cs#L55 + metadata: + uid: PolylineAlgorithm.Abstraction.IPolylineFormatter`2.Write(System.ReadOnlyMemory{System.Char}) + commentId: M:PolylineAlgorithm.Abstraction.IPolylineFormatter`2.Write(System.ReadOnlyMemory{System.Char}) +- markdown: Creates a TPolyline from the encoded character buffer produced by the encoder. +- code: TPolyline Write(ReadOnlyMemory encoded) +- h4: Parameters +- parameters: + - name: encoded + type: + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: The encoded polyline as a read-only memory of characters. +- h4: Returns +- parameters: + - type: + - TPolyline + description: A TPolyline wrapping or derived from encoded. +languageId: csharp +metadata: + description: >- + Defines how to extract and scale values from a TValue for encoding, + + reconstruct a TValue from scaled values for decoding, + + produce a TPolyline from an encoded character buffer, and extract that buffer + + back from a TPolyline. diff --git a/api-reference/0.0/PolylineAlgorithm.Abstraction.yml b/api-reference/0.0/PolylineAlgorithm.Abstraction.yml new file mode 100644 index 00000000..854d4ab0 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.Abstraction.yml @@ -0,0 +1,35 @@ +### YamlMime:ApiPage +title: Namespace PolylineAlgorithm.Abstraction +body: +- api1: Namespace PolylineAlgorithm.Abstraction + id: PolylineAlgorithm_Abstraction + metadata: + uid: PolylineAlgorithm.Abstraction + commentId: N:PolylineAlgorithm.Abstraction +- h3: Interfaces +- parameters: + - type: + text: IPolylineDecoder + url: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.html + description: Defines a contract for decoding an encoded polyline into a sequence of geographic coordinates. + - type: + text: IPolylineEncoder + url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html + description: >- + Contract for encoding a sequence of geographic coordinates into an encoded polyline representation. + + Implementations interpret the generic TValue type and produce an encoded + + representation of those coordinates as TPolyline. + - type: + text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-2.html + description: >- + Defines how to extract and scale values from a TValue for encoding, + + reconstruct a TValue from scaled values for decoding, + + produce a TPolyline from an encoded character buffer, and extract that buffer + + back from a TPolyline. +languageId: csharp diff --git a/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml b/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml new file mode 100644 index 00000000..69b97db4 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml @@ -0,0 +1,102 @@ +### YamlMime:ApiPage +title: Class PolylineEncoderExtensions +body: +- api1: Class PolylineEncoderExtensions + id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L15 + metadata: + uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions + commentId: T:PolylineAlgorithm.Extensions.PolylineEncoderExtensions +- facts: + - name: Namespace + value: + text: PolylineAlgorithm.Extensions + url: PolylineAlgorithm.Extensions.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines. +- code: public static class PolylineEncoderExtensions +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineEncoderExtensions + url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.MemberwiseClone() + url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Methods +- api3: Encode(IPolylineEncoder, TValue[], PolylineEncodingOptions?, CancellationToken) + id: PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___PolylineAlgorithm_PolylineEncodingOptions___0__System_Threading_CancellationToken_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/Extensions/PolylineEncoderExtensions.cs#L33 + metadata: + uid: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},``0[],PolylineAlgorithm.PolylineEncodingOptions{``0},System.Threading.CancellationToken) + commentId: M:PolylineAlgorithm.Extensions.PolylineEncoderExtensions.Encode``2(PolylineAlgorithm.Abstraction.IPolylineEncoder{``0,``1},``0[],PolylineAlgorithm.PolylineEncodingOptions{``0},System.Threading.CancellationToken) +- markdown: Encodes an array of TValue instances into an encoded polyline. +- code: public static TPolyline Encode(this IPolylineEncoder encoder, TValue[] values, PolylineEncodingOptions? options = null, CancellationToken cancellationToken = default) +- h4: Parameters +- parameters: + - name: encoder + type: + - text: IPolylineEncoder + url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: The instance used to perform the encoding operation. + - name: values + type: + - TValue + - '[' + - ']' + description: The array of TValue objects to encode. + - name: options + type: + - text: PolylineEncodingOptions + url: PolylineAlgorithm.PolylineEncodingOptions-1.html + - < + - TValue + - '>' + - '?' + optional: true + - name: cancellationToken + type: + - text: CancellationToken + url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken + optional: true +- h4: Returns +- parameters: + - type: + - TPolyline + description: A TPolyline instance representing the encoded polyline for the provided coordinates. +- h4: Type Parameters +- parameters: + - name: TValue + description: The type that represents a geographic coordinate to encode. + - name: TPolyline + description: The type that represents the encoded polyline output. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when encoder or values is null. +languageId: csharp +metadata: + description: Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines. diff --git a/api-reference/0.0/PolylineAlgorithm.Extensions.yml b/api-reference/0.0/PolylineAlgorithm.Extensions.yml new file mode 100644 index 00000000..4c109ef2 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.Extensions.yml @@ -0,0 +1,15 @@ +### YamlMime:ApiPage +title: Namespace PolylineAlgorithm.Extensions +body: +- api1: Namespace PolylineAlgorithm.Extensions + id: PolylineAlgorithm_Extensions + metadata: + uid: PolylineAlgorithm.Extensions + commentId: N:PolylineAlgorithm.Extensions +- h3: Classes +- parameters: + - type: + text: PolylineEncoderExtensions + url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html + description: Provides extension methods for the interface to facilitate encoding geographic coordinates into polylines. +languageId: csharp diff --git a/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-2.yml b/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-2.yml new file mode 100644 index 00000000..334c7da5 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.FormatterBuilder-2.yml @@ -0,0 +1,354 @@ +### YamlMime:ApiPage +title: Class FormatterBuilder +body: +- api1: Class FormatterBuilder + id: PolylineAlgorithm_FormatterBuilder_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L30 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`2 + commentId: T:PolylineAlgorithm.FormatterBuilder`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Provides a fluent builder for constructing a . +- code: public sealed class FormatterBuilder +- h4: Type Parameters +- parameters: + - name: TValue + description: The coordinate or item type from which column values are extracted. + - name: TPolyline + description: The polyline surface type produced and consumed by the formatter. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-2.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- +

+ + Use to obtain an instance, call once per column, + + optionally chain to set a reference baseline for the most-recently added column, + + optionally chain to register a factory for the decoding direction, + + call to supply the polyline surface delegates (required), then call + + to produce the immutable . + +

+ +

+ + The builder is the only way to create a + + — its constructor is internal. + +

+- h2: Methods +- api3: AddValue(string, Func, uint) + id: PolylineAlgorithm_FormatterBuilder_2_AddValue_System_String_System_Func__0_System_Double__System_UInt32_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L67 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`2.AddValue(System.String,System.Func{`0,System.Double},System.UInt32) + commentId: M:PolylineAlgorithm.FormatterBuilder`2.AddValue(System.String,System.Func{`0,System.Double},System.UInt32) +- markdown: Adds a column with the specified value selector and precision. +- code: public FormatterBuilder AddValue(string name, Func selector, uint precision = 5) +- h4: Parameters +- parameters: + - name: name + type: + - text: string + url: https://learn.microsoft.com/dotnet/api/system.string + description: A unique, non-null, non-empty name that identifies the column. Used for diagnostics only. + - name: selector + type: + - text: Func + url: https://learn.microsoft.com/dotnet/api/system.func-2 + - < + - TValue + - ',' + - " " + - text: double + url: https://learn.microsoft.com/dotnet/api/system.double + - '>' + description: >- + A delegate that extracts the column's raw value from an item of type + + TValue. + - name: precision + type: + - text: uint + url: https://learn.microsoft.com/dotnet/api/system.uint32 + description: >- + The number of decimal places to preserve. Each extracted value is multiplied by + + 10^precision before encoding. Defaults to 5. + optional: true +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: The current builder instance for method chaining. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when name or selector is null. + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when name is empty, or a rule with the same name already exists. +- api3: Build() + id: PolylineAlgorithm_FormatterBuilder_2_Build + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L186 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`2.Build + commentId: M:PolylineAlgorithm.FormatterBuilder`2.Build +- markdown: >- + Bakes all added rules and delegates into a sealed, immutable + + . +- code: public PolylineFormatter Build() +- h4: Returns +- parameters: + - type: + - text: PolylineFormatter + url: PolylineAlgorithm.PolylineFormatter-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: >- + An immutable whose configuration can + + no longer be changed. +- h4: Exceptions +- parameters: + - type: + - text: InvalidOperationException + url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception + description: Thrown when no rules have been added, or when has not been called. +- api3: Create() + id: PolylineAlgorithm_FormatterBuilder_2_Create + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L43 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`2.Create + commentId: M:PolylineAlgorithm.FormatterBuilder`2.Create +- markdown: Creates a new instance. +- code: >- + [SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Factory method on a generic builder intentionally lives on the type so callers write FormatterBuilder.Create() without needing a separate non-generic factory class.")] + + public static FormatterBuilder Create() +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: A fresh builder with no rules and no polyline delegates. +- api3: SetBaseline(long) + id: PolylineAlgorithm_FormatterBuilder_2_SetBaseline_System_Int64_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L106 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`2.SetBaseline(System.Int64) + commentId: M:PolylineAlgorithm.FormatterBuilder`2.SetBaseline(System.Int64) +- markdown: >- + Sets a reference value (baseline) on the most-recently added column. + + During encoding, the baseline is subtracted from the first item's scaled column value so that + + the initial delta is scaled_first_value − baseline rather than scaled_first_value. + + Use this when the absolute scaled value of the first data point for a column would otherwise + + produce a very large initial encoded delta. +- code: public FormatterBuilder SetBaseline(long baseline) +- h4: Parameters +- parameters: + - name: baseline + type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: >- + The reference value to subtract from the first item's scaled column value during encoding. + + The decoder automatically adds this value back, so the reconstructed item matches the + + original input. +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: The current builder instance for method chaining. +- h4: Exceptions +- parameters: + - type: + - text: InvalidOperationException + url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception + description: >- + Thrown when no rules have been added yet. Call before + + . +- api3: WithReaderWriter(Func, TPolyline>, Func>) + id: PolylineAlgorithm_FormatterBuilder_2_WithReaderWriter_System_Func_System_ReadOnlyMemory_System_Char___1__System_Func__1_System_ReadOnlyMemory_System_Char___ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L158 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`2.WithReaderWriter(System.Func{System.ReadOnlyMemory{System.Char},`1},System.Func{`1,System.ReadOnlyMemory{System.Char}}) + commentId: M:PolylineAlgorithm.FormatterBuilder`2.WithReaderWriter(System.Func{System.ReadOnlyMemory{System.Char},`1},System.Func{`1,System.ReadOnlyMemory{System.Char}}) +- markdown: >- + Supplies the polyline-surface delegates required to convert between the raw character buffer + + and a TPolyline. This call is mandatory before . +- code: public FormatterBuilder WithReaderWriter(Func, TPolyline> write, Func> read) +- h4: Parameters +- parameters: + - name: write + type: + - text: Func + url: https://learn.microsoft.com/dotnet/api/system.func-2 + - < + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + - ',' + - " " + - TPolyline + - '>' + description: >- + Converts the encoded of produced by the encoder + + into a TPolyline. + - name: read + type: + - text: Func + url: https://learn.microsoft.com/dotnet/api/system.func-2 + - < + - TPolyline + - ',' + - " " + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + - '>' + description: >- + Extracts the encoded character buffer from a TPolyline for the decoder to + + consume. +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: The current builder instance for method chaining. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when write or read is null. +- api3: WithValueFactory(PolylineItemFactory) + id: PolylineAlgorithm_FormatterBuilder_2_WithValueFactory_PolylineAlgorithm_PolylineItemFactory__0__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/FormatterBuilder.cs#L132 + metadata: + uid: PolylineAlgorithm.FormatterBuilder`2.WithValueFactory(PolylineAlgorithm.PolylineItemFactory{`0}) + commentId: M:PolylineAlgorithm.FormatterBuilder`2.WithValueFactory(PolylineAlgorithm.PolylineItemFactory{`0}) +- markdown: >- + Registers a factory delegate used to reconstruct a TValue from + + denormalized values during decoding. +- code: public FormatterBuilder WithValueFactory(PolylineItemFactory create) +- h4: Parameters +- parameters: + - name: create + type: + - text: PolylineItemFactory + url: PolylineAlgorithm.PolylineItemFactory-1.html + - < + - TValue + - '>' + description: >- + A delegate that accepts the denormalized values reconstructed from the + + polyline and returns a TValue. The formatter automatically divides + + each accumulated scaled integer by its precision factor and adds back any baseline configured + + via , so the span values match the original values supplied to the + + encoder. The span length always equals the number of columns added via . +- h4: Returns +- parameters: + - type: + - text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: The current builder instance for method chaining. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when create is null. +languageId: csharp +metadata: + description: Provides a fluent builder for constructing a . diff --git a/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml b/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml new file mode 100644 index 00000000..7bc75368 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.InvalidPolylineException.yml @@ -0,0 +1,102 @@ +### YamlMime:ApiPage +title: Class InvalidPolylineException +body: +- api1: Class InvalidPolylineException + id: PolylineAlgorithm_InvalidPolylineException + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L17 + metadata: + uid: PolylineAlgorithm.InvalidPolylineException + commentId: T:PolylineAlgorithm.InvalidPolylineException +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Exception thrown when a polyline is determined to be malformed or invalid during processing. +- code: 'public sealed class InvalidPolylineException : Exception, ISerializable' +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: Exception + url: https://learn.microsoft.com/dotnet/api/system.exception + - text: InvalidPolylineException + url: PolylineAlgorithm.InvalidPolylineException.html +- h4: Implements +- list: + - text: ISerializable + url: https://learn.microsoft.com/dotnet/api/system.runtime.serialization.iserializable +- h4: Inherited Members +- list: + - text: Exception.GetBaseException() + url: https://learn.microsoft.com/dotnet/api/system.exception.getbaseexception + - text: Exception.GetObjectData(SerializationInfo, StreamingContext) + url: https://learn.microsoft.com/dotnet/api/system.exception.getobjectdata + - text: Exception.GetType() + url: https://learn.microsoft.com/dotnet/api/system.exception.gettype + - text: Exception.ToString() + url: https://learn.microsoft.com/dotnet/api/system.exception.tostring + - text: Exception.Data + url: https://learn.microsoft.com/dotnet/api/system.exception.data + - text: Exception.HelpLink + url: https://learn.microsoft.com/dotnet/api/system.exception.helplink + - text: Exception.HResult + url: https://learn.microsoft.com/dotnet/api/system.exception.hresult + - text: Exception.InnerException + url: https://learn.microsoft.com/dotnet/api/system.exception.innerexception + - text: Exception.Message + url: https://learn.microsoft.com/dotnet/api/system.exception.message + - text: Exception.Source + url: https://learn.microsoft.com/dotnet/api/system.exception.source + - text: Exception.StackTrace + url: https://learn.microsoft.com/dotnet/api/system.exception.stacktrace + - text: Exception.TargetSite + url: https://learn.microsoft.com/dotnet/api/system.exception.targetsite + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: This exception is used internally to indicate that a polyline string does not conform to the expected format or contains errors. +- h2: Constructors +- api3: InvalidPolylineException() + id: PolylineAlgorithm_InvalidPolylineException__ctor + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L22 + metadata: + uid: PolylineAlgorithm.InvalidPolylineException.#ctor + commentId: M:PolylineAlgorithm.InvalidPolylineException.#ctor +- markdown: Initializes a new instance of the class. +- code: public InvalidPolylineException() +- api3: InvalidPolylineException(string, Exception) + id: PolylineAlgorithm_InvalidPolylineException__ctor_System_String_System_Exception_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/InvalidPolylineException.cs#L43 + metadata: + uid: PolylineAlgorithm.InvalidPolylineException.#ctor(System.String,System.Exception) + commentId: M:PolylineAlgorithm.InvalidPolylineException.#ctor(System.String,System.Exception) +- markdown: Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. +- code: public InvalidPolylineException(string message, Exception innerException) +- h4: Parameters +- parameters: + - name: message + type: + - text: string + url: https://learn.microsoft.com/dotnet/api/system.string + description: The error message that explains the reason for the exception. + - name: innerException + type: + - text: Exception + url: https://learn.microsoft.com/dotnet/api/system.exception + description: The exception that is the cause of the current exception, or a null reference if no inner exception is specified. +languageId: csharp +metadata: + description: Exception thrown when a polyline is determined to be malformed or invalid during processing. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineDecoder-2.yml b/api-reference/0.0/PolylineAlgorithm.PolylineDecoder-2.yml new file mode 100644 index 00000000..789bba03 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineDecoder-2.yml @@ -0,0 +1,165 @@ +### YamlMime:ApiPage +title: Class PolylineDecoder +body: +- api1: Class PolylineDecoder + id: PolylineAlgorithm_PolylineDecoder_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineDecoder.cs#L27 + metadata: + uid: PolylineAlgorithm.PolylineDecoder`2 + commentId: T:PolylineAlgorithm.PolylineDecoder`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Decodes encoded polyline representations into sequences of geographic coordinates. +- code: 'public class PolylineDecoder : IPolylineDecoder' +- h4: Type Parameters +- parameters: + - name: TPolyline + description: The type that represents the encoded polyline input. + - name: TValue + description: The type that represents a decoded geographic coordinate. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineDecoder + url: PolylineAlgorithm.PolylineDecoder-2.html +- h4: Implements +- list: + - text: IPolylineDecoder + url: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.MemberwiseClone() + url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- + Pass a that carries a + + to the constructor. The formatter handles + + all type-specific concerns; no subclassing is required. +- h2: Constructors +- api3: PolylineDecoder(PolylineOptions) + id: PolylineAlgorithm_PolylineDecoder_2__ctor_PolylineAlgorithm_PolylineOptions__1__0__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineDecoder.cs#L41 + metadata: + uid: PolylineAlgorithm.PolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`1,`0}) + commentId: M:PolylineAlgorithm.PolylineDecoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`1,`0}) +- markdown: Initializes a new instance of . +- code: >- + [SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] + + public PolylineDecoder(PolylineOptions options) +- h4: Parameters +- parameters: + - name: options + type: + - text: PolylineOptions + url: PolylineAlgorithm.PolylineOptions-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: >- + A that carries the formatter and settings. + + Must not be null. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when options is null. +- h2: Methods +- api3: Decode(TPolyline, PolylineDecodingOptions?, CancellationToken) + id: PolylineAlgorithm_PolylineDecoder_2_Decode__0_PolylineAlgorithm_PolylineDecodingOptions__1__System_Threading_CancellationToken_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineDecoder.cs#L78 + metadata: + uid: PolylineAlgorithm.PolylineDecoder`2.Decode(`0,PolylineAlgorithm.PolylineDecodingOptions{`1},System.Threading.CancellationToken) + commentId: M:PolylineAlgorithm.PolylineDecoder`2.Decode(`0,PolylineAlgorithm.PolylineDecodingOptions{`1},System.Threading.CancellationToken) +- markdown: >- + Decodes an encoded TPolyline into a sequence of + + TValue instances, applying per-call options to + + seed the accumulated-delta state. Use this overload to decode polylines that were produced by + + chunked encoding. +- code: public IEnumerable Decode(TPolyline polyline, PolylineDecodingOptions? options = null, CancellationToken cancellationToken = default) +- h4: Parameters +- parameters: + - name: polyline + type: + - TPolyline + description: The encoded polyline to decode. Must not be null. + - name: options + type: + - text: PolylineDecodingOptions + url: PolylineAlgorithm.PolylineDecodingOptions-1.html + - < + - TValue + - '>' + - '?' + description: >- + Per-call options that control the accumulated-delta seed. Pass null or an + + instance with set to + + null to start from zero (same as calling + + Decode(TPolyline, CancellationToken)). + optional: true + - name: cancellationToken + type: + - text: CancellationToken + url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken + description: A token that can be used to cancel the operation. + optional: true +- h4: Returns +- parameters: + - type: + - text: IEnumerable + url: https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1 + - < + - TValue + - '>' + description: >- + An of TValue representing the decoded + + coordinates. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when polyline is null. + - type: + - text: InvalidPolylineException + url: PolylineAlgorithm.InvalidPolylineException.html + description: Thrown when the polyline format is invalid or malformed. + - type: + - text: OperationCanceledException + url: https://learn.microsoft.com/dotnet/api/system.operationcanceledexception + description: Thrown when cancellationToken is canceled during decoding. +languageId: csharp +metadata: + description: Decodes encoded polyline representations into sequences of geographic coordinates. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineDecodingOptions-1.yml b/api-reference/0.0/PolylineAlgorithm.PolylineDecodingOptions-1.yml new file mode 100644 index 00000000..297c034e --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineDecodingOptions-1.yml @@ -0,0 +1,117 @@ +### YamlMime:ApiPage +title: Class PolylineDecodingOptions +body: +- api1: Class PolylineDecodingOptions + id: PolylineAlgorithm_PolylineDecodingOptions_1 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineDecodingOptions.cs#L18 + metadata: + uid: PolylineAlgorithm.PolylineDecodingOptions`1 + commentId: T:PolylineAlgorithm.PolylineDecodingOptions`1 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Per-call options for a chunked decoding operation. +- code: public sealed class PolylineDecodingOptions +- h4: Type Parameters +- parameters: + - name: TValue + description: The value type understood by the formatter. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineDecodingOptions + url: PolylineAlgorithm.PolylineDecodingOptions-1.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- + Pass an instance of this class to the chunked + + overload to control + + the accumulated-delta seed used at the start of each chunk. When is + + false zero-initialisation is used, which is the existing default behaviour. +- h2: Constructors +- api3: PolylineDecodingOptions() + id: PolylineAlgorithm_PolylineDecodingOptions_1__ctor + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineDecodingOptions.cs#L24 + metadata: + uid: PolylineAlgorithm.PolylineDecodingOptions`1.#ctor + commentId: M:PolylineAlgorithm.PolylineDecodingOptions`1.#ctor +- markdown: >- + Initializes a new instance of with no + + previous value (zero-initialised baseline will be used). +- code: public PolylineDecodingOptions() +- api3: PolylineDecodingOptions(TValue) + id: PolylineAlgorithm_PolylineDecodingOptions_1__ctor__0_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineDecodingOptions.cs#L33 + metadata: + uid: PolylineAlgorithm.PolylineDecodingOptions`1.#ctor(`0) + commentId: M:PolylineAlgorithm.PolylineDecodingOptions`1.#ctor(`0) +- markdown: >- + Initializes a new instance of with the + + specified previous value used to seed the accumulated-delta state. +- code: public PolylineDecodingOptions(TValue previous) +- h4: Parameters +- parameters: + - name: previous + type: + - TValue + description: The last value of the previous chunk, used to seed the accumulated-delta state. +- h2: Properties +- api3: HasPrevious + id: PolylineAlgorithm_PolylineDecodingOptions_1_HasPrevious + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineDecodingOptions.cs#L43 + metadata: + uid: PolylineAlgorithm.PolylineDecodingOptions`1.HasPrevious + commentId: P:PolylineAlgorithm.PolylineDecodingOptions`1.HasPrevious +- markdown: >- + Gets a value indicating whether a previous value has been supplied to seed the + + accumulated-delta state. When false zero-initialisation is used, which is + + the existing default. +- code: public bool HasPrevious { get; } +- h4: Property Value +- parameters: + - type: + - text: bool + url: https://learn.microsoft.com/dotnet/api/system.boolean +- api3: Previous + id: PolylineAlgorithm_PolylineDecodingOptions_1_Previous + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineDecodingOptions.cs#L49 + metadata: + uid: PolylineAlgorithm.PolylineDecodingOptions`1.Previous + commentId: P:PolylineAlgorithm.PolylineDecodingOptions`1.Previous +- markdown: >- + Gets the last value of the previous chunk, used to seed the accumulated-delta state. + + Only meaningful when is true. +- code: public TValue Previous { get; } +- h4: Property Value +- parameters: + - type: + - TValue +languageId: csharp +metadata: + description: Per-call options for a chunked decoding operation. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncoder-2.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncoder-2.yml new file mode 100644 index 00000000..6c8caa5f --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineEncoder-2.yml @@ -0,0 +1,210 @@ +### YamlMime:ApiPage +title: Class PolylineEncoder +body: +- api1: Class PolylineEncoder + id: PolylineAlgorithm_PolylineEncoder_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoder.cs#L28 + metadata: + uid: PolylineAlgorithm.PolylineEncoder`2 + commentId: T:PolylineAlgorithm.PolylineEncoder`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Encodes sequences of geographic coordinates into encoded polyline representations. +- code: 'public class PolylineEncoder : IPolylineEncoder' +- h4: Type Parameters +- parameters: + - name: TValue + description: The type that represents a geographic coordinate to encode. + - name: TPolyline + description: The type that represents the encoded polyline output. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineEncoder + url: PolylineAlgorithm.PolylineEncoder-2.html +- h4: Implements +- list: + - text: IPolylineEncoder + url: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.MemberwiseClone() + url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h4: Extension Methods +- list: + - text: PolylineEncoderExtensions.Encode(IPolylineEncoder, TValue[], PolylineEncodingOptions?, CancellationToken) + url: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.html#PolylineAlgorithm_Extensions_PolylineEncoderExtensions_Encode__2_PolylineAlgorithm_Abstraction_IPolylineEncoder___0___1____0___PolylineAlgorithm_PolylineEncodingOptions___0__System_Threading_CancellationToken_ +- h2: Remarks +- markdown: >- + Pass a that carries a + + to the constructor. The formatter handles + + all type-specific concerns; no subclassing is required. +- h2: Constructors +- api3: PolylineEncoder(PolylineOptions) + id: PolylineAlgorithm_PolylineEncoder_2__ctor_PolylineAlgorithm_PolylineOptions__0__1__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoder.cs#L43 + metadata: + uid: PolylineAlgorithm.PolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`0,`1}) + commentId: M:PolylineAlgorithm.PolylineEncoder`2.#ctor(PolylineAlgorithm.PolylineOptions{`0,`1}) +- markdown: Initializes a new instance of . +- code: >- + [SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Null is verified before use via ExceptionGuard.ThrowArgumentNull, which is annotated [DoesNotReturn]. CA1062 does not recognise custom [DoesNotReturn] helpers as null guards.")] + + public PolylineEncoder(PolylineOptions options) +- h4: Parameters +- parameters: + - name: options + type: + - text: PolylineOptions + url: PolylineAlgorithm.PolylineOptions-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: >- + A that carries the formatter and settings. + + Must not be null. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when options is null. +- h2: Methods +- api3: Encode(ReadOnlySpan, CancellationToken) + id: PolylineAlgorithm_PolylineEncoder_2_Encode_System_ReadOnlySpan__0__System_Threading_CancellationToken_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoder.cs#L72 + metadata: + uid: PolylineAlgorithm.PolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) + commentId: M:PolylineAlgorithm.PolylineEncoder`2.Encode(System.ReadOnlySpan{`0},System.Threading.CancellationToken) +- markdown: >- + Encodes a collection of TValue instances into an encoded + + TPolyline. +- code: public TPolyline Encode(ReadOnlySpan coordinates, CancellationToken cancellationToken = default) +- h4: Parameters +- parameters: + - name: coordinates + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - TValue + - '>' + description: The collection of coordinates to encode. + - name: cancellationToken + type: + - text: CancellationToken + url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken + description: A token that can be used to cancel the operation. + optional: true +- h4: Returns +- parameters: + - type: + - TPolyline + description: An instance of TPolyline representing the encoded coordinates. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when coordinates is empty. + - type: + - text: InvalidOperationException + url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception + description: Thrown when the internal encoding buffer cannot accommodate the encoded value. + - type: + - text: OperationCanceledException + url: https://learn.microsoft.com/dotnet/api/system.operationcanceledexception + description: Thrown when cancellationToken is canceled. +- api3: Encode(ReadOnlySpan, PolylineEncodingOptions?, CancellationToken) + id: PolylineAlgorithm_PolylineEncoder_2_Encode_System_ReadOnlySpan__0__PolylineAlgorithm_PolylineEncodingOptions__0__System_Threading_CancellationToken_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoder.cs#L158 + metadata: + uid: PolylineAlgorithm.PolylineEncoder`2.Encode(System.ReadOnlySpan{`0},PolylineAlgorithm.PolylineEncodingOptions{`0},System.Threading.CancellationToken) + commentId: M:PolylineAlgorithm.PolylineEncoder`2.Encode(System.ReadOnlySpan{`0},PolylineAlgorithm.PolylineEncodingOptions{`0},System.Threading.CancellationToken) +- markdown: >- + Encodes a collection of TValue instances into an encoded + + TPolyline, applying per-call options to control the + + delta baseline. Use this overload to encode large sequences in independent chunks that can be + + concatenated into a single valid polyline. +- code: public TPolyline Encode(ReadOnlySpan coordinates, PolylineEncodingOptions? options, CancellationToken cancellationToken) +- h4: Parameters +- parameters: + - name: coordinates + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - TValue + - '>' + description: The collection of coordinates to encode. + - name: options + type: + - text: PolylineEncodingOptions + url: PolylineAlgorithm.PolylineEncodingOptions-1.html + - < + - TValue + - '>' + - '?' + description: >- + Per-call options that control the starting delta baseline. Pass null or an + + instance with set to + + null to use the formatter's default baseline (same as calling + + ). + - name: cancellationToken + type: + - text: CancellationToken + url: https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken + description: A token that can be used to cancel the operation. +- h4: Returns +- parameters: + - type: + - TPolyline + description: An instance of TPolyline representing the encoded coordinates. +- h4: Exceptions +- parameters: + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when coordinates is empty. + - type: + - text: InvalidOperationException + url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception + description: Thrown when the internal encoding buffer cannot accommodate the encoded value. + - type: + - text: OperationCanceledException + url: https://learn.microsoft.com/dotnet/api/system.operationcanceledexception + description: Thrown when cancellationToken is canceled. +languageId: csharp +metadata: + description: Encodes sequences of geographic coordinates into encoded polyline representations. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml new file mode 100644 index 00000000..f5f6f243 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineEncoding.yml @@ -0,0 +1,527 @@ +### YamlMime:ApiPage +title: Class PolylineEncoding +body: +- api1: Class PolylineEncoding + id: PolylineAlgorithm_PolylineEncoding + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L23 + metadata: + uid: PolylineAlgorithm.PolylineEncoding + commentId: T:PolylineAlgorithm.PolylineEncoding +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and de-normalizing + + geographic coordinate values. +- code: public static class PolylineEncoding +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineEncoding + url: PolylineAlgorithm.PolylineEncoding.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.MemberwiseClone() + url: https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- + The class includes functionality for working with encoded polyline + data, such as reading and writing encoded values, as well as methods for normalizing and de-normalizing geographic + coordinates. It also provides validation utilities to ensure values conform to expected ranges for latitude and + longitude. +- h2: Methods +- api3: Denormalize(long, uint) + id: PolylineAlgorithm_PolylineEncoding_Denormalize_System_Int64_System_UInt32_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L119 + metadata: + uid: PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int64,System.UInt32) + commentId: M:PolylineAlgorithm.PolylineEncoding.Denormalize(System.Int64,System.UInt32) +- markdown: Converts a normalized integer coordinate value back to its floating-point representation based on the specified precision. +- code: public static double Denormalize(long value, uint precision = 5) +- h4: Parameters +- parameters: + - name: value + type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: The long integer value to denormalize. Typically produced by the method. + - name: precision + type: + - text: uint + url: https://learn.microsoft.com/dotnet/api/system.uint32 + description: The number of decimal places used during normalization. Default is 5, matching standard polyline encoding precision. + optional: true +- h4: Returns +- parameters: + - type: + - text: double + url: https://learn.microsoft.com/dotnet/api/system.double + description: The denormalized floating-point coordinate value. +- h4: Remarks +- markdown: >- +

+ + This method reverses the normalization performed by . It takes an integer value and converts it + + to a double by dividing it by 10 raised to the power of the specified precision. If precision is 0, + + the value is returned as a double without division. + +

+ +

+ + The calculation is performed inside a checked block to ensure that any arithmetic overflow is detected + + and an is thrown. + +

+ +

+ + For example, with a precision of 5: + + +

  • A value of 3778903 becomes 37.78903
  • A value of -12241230 becomes -122.4123
+ +

+ +

+ + If the input value is 0, the method returns 0.0 immediately. + +

+- h4: Exceptions +- parameters: + - type: + - text: OverflowException + url: https://learn.microsoft.com/dotnet/api/system.overflowexception + description: Thrown if the arithmetic operation overflows during conversion. +- api3: GetRequiredBufferSize(long) + id: PolylineAlgorithm_PolylineEncoding_GetRequiredBufferSize_System_Int64_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L296 + metadata: + uid: PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(System.Int64) + commentId: M:PolylineAlgorithm.PolylineEncoding.GetRequiredBufferSize(System.Int64) +- markdown: Calculates the number of characters required to encode a delta value in polyline format. +- code: public static int GetRequiredBufferSize(long delta) +- h4: Parameters +- parameters: + - name: delta + type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: >- + The long delta value to calculate the encoded size for. This value typically represents the difference between + + consecutive coordinate values in polyline encoding. +- h4: Returns +- parameters: + - type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + description: The number of characters required to encode the specified delta value. The minimum return value is 1. +- h4: Remarks +- markdown: >- +

+ + This method determines how many characters will be needed to represent an integer delta value when encoded + + using the polyline encoding algorithm. It performs the same zigzag encoding transformation as + + but only calculates the required buffer size without actually writing any data. + +

+ +

+ + The calculation process: + + +

  1. Applies zigzag encoding: left-shifts the value by 1 bit, then inverts all bits if the original value was negative
  2. Counts how many 5-bit chunks are needed to represent the encoded value
  3. Each chunk requires one character, with a minimum of 1 character for any value
+ +

+ +

+ + This method is useful for pre-allocating buffers of the correct size before encoding polyline data, helping to avoid + + buffer overflow checks during the actual encoding process. + +

+ +

+ + The method uses ulong internally to handle the full range of 64-bit signed values correctly + + during the zigzag encoding step. + +

+- h4: See Also +- list: + - - text: PolylineEncoding + url: PolylineAlgorithm.PolylineEncoding.html + - . + - text: TryWriteValue + url: PolylineAlgorithm.PolylineEncoding.html#PolylineAlgorithm_PolylineEncoding_TryWriteValue_System_Int64_System_Span_System_Char__System_Int32__ + - ( + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - ',' + - " " + - text: Span + url: https://learn.microsoft.com/dotnet/api/system.span-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + - ',' + - " " + - ref + - " " + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + - ) +- api3: Normalize(double, uint) + id: PolylineAlgorithm_PolylineEncoding_Normalize_System_Double_System_UInt32_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L58 + metadata: + uid: PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,System.UInt32) + commentId: M:PolylineAlgorithm.PolylineEncoding.Normalize(System.Double,System.UInt32) +- markdown: Normalizes a geographic coordinate value to an integer representation based on the specified precision. +- code: public static long Normalize(double value, uint precision = 5) +- h4: Parameters +- parameters: + - name: value + type: + - text: double + url: https://learn.microsoft.com/dotnet/api/system.double + description: The numeric value to normalize. Must be a finite number (not NaN or infinity). + - name: precision + type: + - text: uint + url: https://learn.microsoft.com/dotnet/api/system.uint32 + description: >- + The number of decimal places of precision to preserve in the normalized value. + + The value is multiplied by 10^precision before rounding. + + Default is 5, which is standard for polyline encoding. + optional: true +- h4: Returns +- parameters: + - type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: A long integer representing the normalized value. Returns 0 if the input value is 0.0. +- h4: Remarks +- markdown: >- +

+ + 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 truncating the result to an integer. + +

+ +

+ + For example, with the default precision of 5: + + +

  • A value of 37.78903 becomes 3778903
  • A value of -122.4123 becomes -12241230
+ +

+ +

+ + The method validates that the input value is finite (not NaN or infinity) before performing normalization. + + If the precision is 0, the value is rounded without multiplication. + +

+- h4: Exceptions +- parameters: + - type: + - text: ArgumentOutOfRangeException + url: https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception + description: Thrown when value is not a finite number (NaN or infinity). +- api3: TryReadValue(ref long, ReadOnlyMemory, ref int) + id: PolylineAlgorithm_PolylineEncoding_TryReadValue_System_Int64__System_ReadOnlyMemory_System_Char__System_Int32__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L166 + metadata: + uid: PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int64@,System.ReadOnlyMemory{System.Char},System.Int32@) + commentId: M:PolylineAlgorithm.PolylineEncoding.TryReadValue(System.Int64@,System.ReadOnlyMemory{System.Char},System.Int32@) +- markdown: Attempts to read an encoded integer value from a polyline buffer, updating the specified delta and position. +- code: public static bool TryReadValue(ref long delta, ReadOnlyMemory buffer, ref int position) +- h4: Parameters +- parameters: + - name: delta + type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: Reference to the long accumulator that will be updated with the decoded value. + - name: buffer + type: + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: The buffer containing polyline-encoded characters. + - name: position + type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + description: Reference to the current position in the buffer. This value is updated as characters are read. +- h4: Returns +- parameters: + - type: + - text: bool + url: https://learn.microsoft.com/dotnet/api/system.boolean + description: true if a value was successfully read and decoded; false if the buffer ended before a complete value was read. +- h4: Remarks +- markdown: >- +

+ + This method decodes a value from a polyline-encoded character buffer, starting at the given position. It reads + + characters sequentially, applying the polyline decoding algorithm, and updates the delta with + + the decoded value. The position is advanced as characters are processed. + +

+ +

+ + The decoding process continues until a character with a value less than the algorithm's space constant is encountered, + + which signals the end of the encoded value. If the buffer is exhausted before a complete value is read, the method returns false. + +

+ +

+ + The decoded value is added to delta using zigzag decoding, which handles both positive and negative values. + +

+- api3: TryWriteValue(long, Span, ref int) + id: PolylineAlgorithm_PolylineEncoding_TryWriteValue_System_Int64_System_Span_System_Char__System_Int32__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L234 + metadata: + uid: PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int64,System.Span{System.Char},System.Int32@) + commentId: M:PolylineAlgorithm.PolylineEncoding.TryWriteValue(System.Int64,System.Span{System.Char},System.Int32@) +- markdown: Attempts to write an encoded integer value to a polyline buffer, updating the specified position. +- code: public static bool TryWriteValue(long delta, Span buffer, ref int position) +- h4: Parameters +- parameters: + - name: delta + type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: >- + The long value to encode and write to the buffer. This value typically represents the difference between consecutive + + coordinate values in polyline encoding. + - name: buffer + type: + - text: Span + url: https://learn.microsoft.com/dotnet/api/system.span-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: The destination buffer where the encoded characters will be written. Must have sufficient capacity to hold the encoded value. + - name: position + type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + description: >- + Reference to the current position in the buffer. This value is updated as characters are written to reflect the new position + + after encoding is complete. +- h4: Returns +- parameters: + - type: + - text: bool + url: https://learn.microsoft.com/dotnet/api/system.boolean + description: >- + true if the value was successfully encoded and written to the buffer; false if the buffer + + does not have sufficient remaining capacity to hold the encoded value. +- h4: Remarks +- markdown: >- +

+ + This method encodes an integer delta value into a polyline-encoded format and writes it to the provided character buffer, + + starting at the given position. It applies zigzag encoding followed by the polyline encoding algorithm to represent + + both positive and negative values efficiently. + +

+ +

+ + The encoding process first converts the value using zigzag encoding (left shift by 1, with bitwise inversion for negative values), + + then writes it as a sequence of characters. Each character encodes 5 bits of data, with continuation bits indicating whether + + more characters follow. The position is advanced as characters are written. + +

+ +

+ + Before writing, the method validates that sufficient space is available in the buffer by calling . + + If the buffer does not have enough remaining capacity, the method returns false without modifying the buffer or position. + +

+ +

+ + This method is the inverse of and can be used to encode coordinate deltas for polyline serialization. + +

+- api3: ValidateBlockLength(ReadOnlySpan) + id: PolylineAlgorithm_PolylineEncoding_ValidateBlockLength_System_ReadOnlySpan_System_Char__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L436 + metadata: + uid: PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan{System.Char}) + commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateBlockLength(System.ReadOnlySpan{System.Char}) +- markdown: Validates the block structure of a polyline segment, ensuring each encoded value does not exceed 7 characters and the polyline ends correctly. +- code: public static void ValidateBlockLength(ReadOnlySpan polyline) +- h4: Parameters +- parameters: + - name: polyline + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: A span representing the polyline segment to validate. +- h4: Remarks +- markdown: >- +

+ + Iterates through the polyline, counting the length of each block (a sequence of characters representing an encoded value). + + Throws an if any block exceeds 7 characters or if the polyline does not end with a valid block terminator. + +

+- h4: Exceptions +- parameters: + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when a block exceeds 7 characters or the polyline does not end with a valid block terminator. +- api3: ValidateCharRange(ReadOnlySpan) + id: PolylineAlgorithm_PolylineEncoding_ValidateCharRange_System_ReadOnlySpan_System_Char__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L390 + metadata: + uid: PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan{System.Char}) + commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateCharRange(System.ReadOnlySpan{System.Char}) +- markdown: Validates that all characters in the polyline segment are within the allowed ASCII range for polyline encoding. +- code: public static void ValidateCharRange(ReadOnlySpan polyline) +- h4: Parameters +- parameters: + - name: polyline + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: A span representing the polyline segment to validate. +- h4: Remarks +- markdown: >- +

+ + Uses SIMD vectorization for efficient validation of large spans. Falls back to scalar checks for any block where an invalid character is detected. + +

+ +

+ + The valid range is from '?' (63) to '_' (95), inclusive. If an invalid character is found, an is thrown. + +

+- h4: Exceptions +- parameters: + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when an invalid character is found in the polyline segment. +- api3: ValidateFormat(ReadOnlySpan) + id: PolylineAlgorithm_PolylineEncoding_ValidateFormat_System_ReadOnlySpan_System_Char__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncoding.cs#L368 + metadata: + uid: PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan{System.Char}) + commentId: M:PolylineAlgorithm.PolylineEncoding.ValidateFormat(System.ReadOnlySpan{System.Char}) +- markdown: Validates the format of a polyline segment, ensuring all characters are valid and block structure is correct. +- code: public static void ValidateFormat(ReadOnlySpan polyline) +- h4: Parameters +- parameters: + - name: polyline + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: A span representing the polyline segment to validate. +- h4: Remarks +- markdown: >- +

+ + This method performs two levels of validation on the provided polyline segment: + +

+ +
  1. + Character Range Validation: Checks that every character in the polyline is within the valid ASCII range for polyline encoding ('?' [63] to '_' [95], inclusive). + Uses SIMD acceleration for efficient validation of large segments. +
  2. + Block Structure Validation: Ensures that each encoded value (block) does not exceed 7 characters and that the polyline ends with a valid block terminator. +
+

+ + If an invalid character or block structure is detected, an is thrown with details about the error. + +

+- h4: Exceptions +- parameters: + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when an invalid character is found or the block structure is invalid. +languageId: csharp +metadata: + description: >- + Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and de-normalizing + + geographic coordinate values. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions-1.yml b/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions-1.yml new file mode 100644 index 00000000..e9f6d504 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineEncodingOptions-1.yml @@ -0,0 +1,121 @@ +### YamlMime:ApiPage +title: Class PolylineEncodingOptions +body: +- api1: Class PolylineEncodingOptions + id: PolylineAlgorithm_PolylineEncodingOptions_1 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L19 + metadata: + uid: PolylineAlgorithm.PolylineEncodingOptions`1 + commentId: T:PolylineAlgorithm.PolylineEncodingOptions`1 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Per-call options for a chunked encoding operation. +- code: public sealed class PolylineEncodingOptions +- h4: Type Parameters +- parameters: + - name: TValue + description: The coordinate type understood by the formatter. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineEncodingOptions + url: PolylineAlgorithm.PolylineEncodingOptions-1.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- + Pass an instance of this class to the chunked + + overload to control + + the delta baseline used at the start of each chunk. When is + + false the formatter's built-in baseline (or zero) is used, which is equivalent + + to the existing default behaviour. +- h2: Constructors +- api3: PolylineEncodingOptions() + id: PolylineAlgorithm_PolylineEncodingOptions_1__ctor + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L26 + metadata: + uid: PolylineAlgorithm.PolylineEncodingOptions`1.#ctor + commentId: M:PolylineAlgorithm.PolylineEncodingOptions`1.#ctor +- markdown: >- + Initializes a new instance of with no + + previous coordinate (formatter default baseline will be used). +- code: public PolylineEncodingOptions() +- api3: PolylineEncodingOptions(TValue) + id: PolylineAlgorithm_PolylineEncodingOptions_1__ctor__0_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L35 + metadata: + uid: PolylineAlgorithm.PolylineEncodingOptions`1.#ctor(`0) + commentId: M:PolylineAlgorithm.PolylineEncodingOptions`1.#ctor(`0) +- markdown: >- + Initializes a new instance of with the + + specified previous coordinate used to seed the delta baseline. +- code: public PolylineEncodingOptions(TValue previous) +- h4: Parameters +- parameters: + - name: previous + type: + - TValue + description: The last coordinate of the previous chunk, used to seed the delta baseline. +- h2: Properties +- api3: HasPrevious + id: PolylineAlgorithm_PolylineEncodingOptions_1_HasPrevious + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L46 + metadata: + uid: PolylineAlgorithm.PolylineEncodingOptions`1.HasPrevious + commentId: P:PolylineAlgorithm.PolylineEncodingOptions`1.HasPrevious +- markdown: >- + Gets a value indicating whether a previous coordinate has been supplied to seed the delta + + baseline. When false the formatter's built-in baseline is used as the + + starting point (which defaults to zero when no baseline has been configured), equivalent to + + the existing default behaviour. +- code: public bool HasPrevious { get; } +- h4: Property Value +- parameters: + - type: + - text: bool + url: https://learn.microsoft.com/dotnet/api/system.boolean +- api3: Previous + id: PolylineAlgorithm_PolylineEncodingOptions_1_Previous + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineEncodingOptions.cs#L52 + metadata: + uid: PolylineAlgorithm.PolylineEncodingOptions`1.Previous + commentId: P:PolylineAlgorithm.PolylineEncodingOptions`1.Previous +- markdown: >- + Gets the last coordinate of the previous chunk, used to seed the delta baseline. + + Only meaningful when is true. +- code: public TValue Previous { get; } +- h4: Property Value +- parameters: + - type: + - TValue +languageId: csharp +metadata: + description: Per-call options for a chunked encoding operation. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineFormatter-2.yml b/api-reference/0.0/PolylineAlgorithm.PolylineFormatter-2.yml new file mode 100644 index 00000000..22a67648 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineFormatter-2.yml @@ -0,0 +1,229 @@ +### YamlMime:ApiPage +title: Class PolylineFormatter +body: +- api1: Class PolylineFormatter + id: PolylineAlgorithm_PolylineFormatter_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L23 + metadata: + uid: PolylineAlgorithm.PolylineFormatter`2 + commentId: T:PolylineAlgorithm.PolylineFormatter`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: A sealed, immutable formatter that implements . +- code: 'public sealed class PolylineFormatter : IPolylineFormatter' +- h4: Type Parameters +- parameters: + - name: TValue + description: The coordinate or item type. + - name: TPolyline + description: The polyline surface type. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineFormatter + url: PolylineAlgorithm.PolylineFormatter-2.html +- h4: Implements +- list: + - text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-2.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- + Instances are constructed exclusively through . + + The sealed modifier allows the JIT to devirtualise and inline calls to the + + interface methods in the encoding/decoding hot loop. +- h2: Properties +- api3: Width + id: PolylineAlgorithm_PolylineFormatter_2_Width + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L46 + metadata: + uid: PolylineAlgorithm.PolylineFormatter`2.Width + commentId: P:PolylineAlgorithm.PolylineFormatter`2.Width +- markdown: >- + Gets the number of values (columns) per encoded item. + + This is the required length of the passed to + + and the length of the span received in . +- code: public int Width { get; } +- h4: Property Value +- parameters: + - type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 +- h2: Methods +- api3: CreateItem(ReadOnlySpan) + id: PolylineAlgorithm_PolylineFormatter_2_CreateItem_System_ReadOnlySpan_System_Int64__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L84 + metadata: + uid: PolylineAlgorithm.PolylineFormatter`2.CreateItem(System.ReadOnlySpan{System.Int64}) + commentId: M:PolylineAlgorithm.PolylineFormatter`2.CreateItem(System.ReadOnlySpan{System.Int64}) +- markdown: >- + Reconstructs a TValue from the given accumulated scaled integer values. + + Called once per decoded item in the decoding loop. Implementations are responsible for + + denormalizing the raw scaled integers (e.g. dividing by the precision factor and adding back + + any baseline) before constructing the item. +- code: public TValue CreateItem(ReadOnlySpan values) +- h4: Parameters +- parameters: + - name: values + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: >- + The raw accumulated scaled integer values decoded from the polyline. Each element corresponds to + + the same column position as in . These are the direct output of the + + delta-accumulation loop in the decoder before any denormalization is applied. +- h4: Returns +- parameters: + - type: + - TValue + description: A TValue reconstructed from values. +- h4: Exceptions +- parameters: + - type: + - text: InvalidOperationException + url: https://learn.microsoft.com/dotnet/api/system.invalidoperationexception + description: >- + Thrown when no factory delegate was supplied via + + . +- api3: GetBaseline(int) + id: PolylineAlgorithm_PolylineFormatter_2_GetBaseline_System_Int32_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L49 + metadata: + uid: PolylineAlgorithm.PolylineFormatter`2.GetBaseline(System.Int32) + commentId: M:PolylineAlgorithm.PolylineFormatter`2.GetBaseline(System.Int32) +- markdown: >- + Returns the baseline for the column at index, or 0 if none is configured. + + The encoder uses this as the starting point for the first item's delta computation: the initial + + delta for the column is scaled_first_value − baseline rather than scaled_first_value. +- code: public long GetBaseline(int index) +- h4: Parameters +- parameters: + - name: index + type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + description: The zero-based column index. Must be in the range [0, ). +- h4: Returns +- parameters: + - type: + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + description: The baseline value, or 0 when no baseline has been defined for the column. +- api3: GetValues(TValue, Span) + id: PolylineAlgorithm_PolylineFormatter_2_GetValues__0_System_Span_System_Int64__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L56 + metadata: + uid: PolylineAlgorithm.PolylineFormatter`2.GetValues(`0,System.Span{System.Int64}) + commentId: M:PolylineAlgorithm.PolylineFormatter`2.GetValues(`0,System.Span{System.Int64}) +- markdown: >- + Extracts and scales all column values from item into the values span. + + Called once per item in the encoding loop. +- code: public void GetValues(TValue item, Span values) +- h4: Parameters +- parameters: + - name: item + type: + - TValue + description: The source item from which column values are extracted. + - name: values + type: + - text: Span + url: https://learn.microsoft.com/dotnet/api/system.span-1 + - < + - text: long + url: https://learn.microsoft.com/dotnet/api/system.int64 + - '>' + description: Output buffer that receives the scaled integer values. Its length must equal . +- h4: Exceptions +- parameters: + - type: + - text: ArgumentException + url: https://learn.microsoft.com/dotnet/api/system.argumentexception + description: Thrown when values.Length does not equal . +- api3: Read(TPolyline) + id: PolylineAlgorithm_PolylineFormatter_2_Read__1_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L76 + metadata: + uid: PolylineAlgorithm.PolylineFormatter`2.Read(`1) + commentId: M:PolylineAlgorithm.PolylineFormatter`2.Read(`1) +- markdown: Extracts the character buffer from a TPolyline for the decoder to read. +- code: public ReadOnlyMemory Read(TPolyline polyline) +- h4: Parameters +- parameters: + - name: polyline + type: + - TPolyline + description: The polyline to read from. +- h4: Returns +- parameters: + - type: + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: A of representing the encoded characters. +- api3: Write(ReadOnlyMemory) + id: PolylineAlgorithm_PolylineFormatter_2_Write_System_ReadOnlyMemory_System_Char__ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineFormatter.cs#L72 + metadata: + uid: PolylineAlgorithm.PolylineFormatter`2.Write(System.ReadOnlyMemory{System.Char}) + commentId: M:PolylineAlgorithm.PolylineFormatter`2.Write(System.ReadOnlyMemory{System.Char}) +- markdown: Creates a TPolyline from the encoded character buffer produced by the encoder. +- code: public TPolyline Write(ReadOnlyMemory encoded) +- h4: Parameters +- parameters: + - name: encoded + type: + - text: ReadOnlyMemory + url: https://learn.microsoft.com/dotnet/api/system.readonlymemory-1 + - < + - text: char + url: https://learn.microsoft.com/dotnet/api/system.char + - '>' + description: The encoded polyline as a read-only memory of characters. +- h4: Returns +- parameters: + - type: + - TPolyline + description: A TPolyline wrapping or derived from encoded. +languageId: csharp +metadata: + description: A sealed, immutable formatter that implements . diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml b/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml new file mode 100644 index 00000000..6b583486 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineItemFactory-1.yml @@ -0,0 +1,54 @@ +### YamlMime:ApiPage +title: Delegate PolylineItemFactory +body: +- api1: Delegate PolylineItemFactory + id: PolylineAlgorithm_PolylineItemFactory_1 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineItemFactory.cs#L22 + metadata: + uid: PolylineAlgorithm.PolylineItemFactory`1 + commentId: T:PolylineAlgorithm.PolylineItemFactory`1 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: >- + Represents a factory method that reconstructs a T item from denormalized + + values decoded from a polyline. +- code: public delegate T PolylineItemFactory(ReadOnlySpan values) +- h4: Parameters +- parameters: + - name: values + type: + - text: ReadOnlySpan + url: https://learn.microsoft.com/dotnet/api/system.readonlyspan-1 + - < + - text: double + url: https://learn.microsoft.com/dotnet/api/system.double + - '>' + description: >- + The denormalized values reconstructed by the polyline decoder. Each element corresponds to the + + original value that was encoded, with the precision factor divided out and any + + baseline added back. The span length equals the number of columns defined via + + . +- h4: Returns +- parameters: + - type: + - T + description: A T instance reconstructed from values. +- h4: Type Parameters +- parameters: + - name: T + description: The coordinate or item type to create. +languageId: csharp +metadata: + description: >- + Represents a factory method that reconstructs a T item from denormalized + + values decoded from a polyline. diff --git a/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml b/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml new file mode 100644 index 00000000..7b80a134 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.PolylineOptions-2.yml @@ -0,0 +1,158 @@ +### YamlMime:ApiPage +title: Class PolylineOptions +body: +- api1: Class PolylineOptions + id: PolylineAlgorithm_PolylineOptions_2 + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L23 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2 + commentId: T:PolylineAlgorithm.PolylineOptions`2 +- facts: + - name: Namespace + value: + text: PolylineAlgorithm + url: PolylineAlgorithm.html + - name: Assembly + value: PolylineAlgorithm.dll +- markdown: Provides unified configuration for a formatter-driven encoding or decoding operation. +- code: public sealed class PolylineOptions +- h4: Type Parameters +- parameters: + - name: TValue + description: The coordinate or item type understood by the formatter. + - name: TPolyline + description: The polyline surface type understood by the formatter. +- h4: Inheritance +- inheritance: + - text: object + url: https://learn.microsoft.com/dotnet/api/system.object + - text: PolylineOptions + url: PolylineAlgorithm.PolylineOptions-2.html +- h4: Inherited Members +- list: + - text: object.Equals(object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object) + - text: object.Equals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object) + - text: object.GetHashCode() + url: https://learn.microsoft.com/dotnet/api/system.object.gethashcode + - text: object.GetType() + url: https://learn.microsoft.com/dotnet/api/system.object.gettype + - text: object.ReferenceEquals(object, object) + url: https://learn.microsoft.com/dotnet/api/system.object.referenceequals + - text: object.ToString() + url: https://learn.microsoft.com/dotnet/api/system.object.tostring +- h2: Remarks +- markdown: >- + Supply an and optional settings, + + then pass this instance to and/or + + . +- h2: Constructors +- api3: PolylineOptions(IPolylineFormatter, int, ILoggerFactory?) + id: PolylineAlgorithm_PolylineOptions_2__ctor_PolylineAlgorithm_Abstraction_IPolylineFormatter__0__1__System_Int32_Microsoft_Extensions_Logging_ILoggerFactory_ + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L41 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2.#ctor(PolylineAlgorithm.Abstraction.IPolylineFormatter{`0,`1},System.Int32,Microsoft.Extensions.Logging.ILoggerFactory) + commentId: M:PolylineAlgorithm.PolylineOptions`2.#ctor(PolylineAlgorithm.Abstraction.IPolylineFormatter{`0,`1},System.Int32,Microsoft.Extensions.Logging.ILoggerFactory) +- markdown: Initializes a new instance of . +- code: public PolylineOptions(IPolylineFormatter formatter, int stackAllocLimit = 512, ILoggerFactory? loggerFactory = null) +- h4: Parameters +- parameters: + - name: formatter + type: + - text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' + description: >- + The unified formatter that handles all type-specific concerns: value extraction, item + + reconstruction, and polyline surface conversion. Must not be null. + - name: stackAllocLimit + type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 + description: The maximum buffer size (in characters) for stack allocation. Defaults to 512. + optional: true + - name: loggerFactory + type: + - text: ILoggerFactory + url: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerfactory + - '?' + description: >- + The logger factory for diagnostic logging. Pass null to use + + . + optional: true +- h4: Exceptions +- parameters: + - type: + - text: ArgumentNullException + url: https://learn.microsoft.com/dotnet/api/system.argumentnullexception + description: Thrown when formatter is null. +- h2: Properties +- api3: Formatter + id: PolylineAlgorithm_PolylineOptions_2_Formatter + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L58 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2.Formatter + commentId: P:PolylineAlgorithm.PolylineOptions`2.Formatter +- markdown: >- + Gets the unified formatter that handles value extraction, item reconstruction, and polyline + + surface conversion. +- code: public IPolylineFormatter Formatter { get; } +- h4: Property Value +- parameters: + - type: + - text: IPolylineFormatter + url: PolylineAlgorithm.Abstraction.IPolylineFormatter-2.html + - < + - TValue + - ',' + - " " + - TPolyline + - '>' +- api3: LoggerFactory + id: PolylineAlgorithm_PolylineOptions_2_LoggerFactory + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L71 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2.LoggerFactory + commentId: P:PolylineAlgorithm.PolylineOptions`2.LoggerFactory +- markdown: >- + Gets the logger factory used for diagnostic logging during encoding and decoding operations. + + Defaults to . +- code: public ILoggerFactory LoggerFactory { get; } +- h4: Property Value +- parameters: + - type: + - text: ILoggerFactory + url: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerfactory +- api3: StackAllocLimit + id: PolylineAlgorithm_PolylineOptions_2_StackAllocLimit + src: https://github.com/petesramek/polyline-algorithm-csharp/blob/copilot/extend-polylinealgorithm-multi-dimensional-support/src/PolylineAlgorithm/PolylineOptions.cs#L65 + metadata: + uid: PolylineAlgorithm.PolylineOptions`2.StackAllocLimit + commentId: P:PolylineAlgorithm.PolylineOptions`2.StackAllocLimit +- markdown: >- + Gets the maximum buffer size (in characters) that may be allocated on the stack for encoding. + + When the required buffer size exceeds this limit, memory is rented from + + instead. Defaults to 512. +- code: public int StackAllocLimit { get; } +- h4: Property Value +- parameters: + - type: + - text: int + url: https://learn.microsoft.com/dotnet/api/system.int32 +languageId: csharp +metadata: + description: Provides unified configuration for a formatter-driven encoding or decoding operation. diff --git a/api-reference/0.0/PolylineAlgorithm.yml b/api-reference/0.0/PolylineAlgorithm.yml new file mode 100644 index 00000000..3c1add37 --- /dev/null +++ b/api-reference/0.0/PolylineAlgorithm.yml @@ -0,0 +1,67 @@ +### YamlMime:ApiPage +title: Namespace PolylineAlgorithm +body: +- api1: Namespace PolylineAlgorithm + id: PolylineAlgorithm + metadata: + uid: PolylineAlgorithm + commentId: N:PolylineAlgorithm +- h3: Namespaces +- parameters: + - type: + text: PolylineAlgorithm.Abstraction + url: PolylineAlgorithm.Abstraction.html + - type: + text: PolylineAlgorithm.Extensions + url: PolylineAlgorithm.Extensions.html +- h3: Classes +- parameters: + - type: + text: FormatterBuilder + url: PolylineAlgorithm.FormatterBuilder-2.html + description: Provides a fluent builder for constructing a . + - type: + text: InvalidPolylineException + url: PolylineAlgorithm.InvalidPolylineException.html + description: Exception thrown when a polyline is determined to be malformed or invalid during processing. + - type: + text: PolylineDecoder + url: PolylineAlgorithm.PolylineDecoder-2.html + description: Decodes encoded polyline representations into sequences of geographic coordinates. + - type: + text: PolylineDecodingOptions + url: PolylineAlgorithm.PolylineDecodingOptions-1.html + description: Per-call options for a chunked decoding operation. + - type: + text: PolylineEncoder + url: PolylineAlgorithm.PolylineEncoder-2.html + description: Encodes sequences of geographic coordinates into encoded polyline representations. + - type: + text: PolylineEncoding + url: PolylineAlgorithm.PolylineEncoding.html + description: >- + Provides methods for encoding and decoding polyline data, as well as utilities for normalizing and de-normalizing + + geographic coordinate values. + - type: + text: PolylineEncodingOptions + url: PolylineAlgorithm.PolylineEncodingOptions-1.html + description: Per-call options for a chunked encoding operation. + - type: + text: PolylineFormatter + url: PolylineAlgorithm.PolylineFormatter-2.html + description: A sealed, immutable formatter that implements . + - type: + text: PolylineOptions + url: PolylineAlgorithm.PolylineOptions-2.html + description: Provides unified configuration for a formatter-driven encoding or decoding operation. +- h3: Delegates +- parameters: + - type: + text: PolylineItemFactory + url: PolylineAlgorithm.PolylineItemFactory-1.html + description: >- + Represents a factory method that reconstructs a T item from denormalized + + values decoded from a polyline. +languageId: csharp diff --git a/api-reference/0.0/toc.yml b/api-reference/0.0/toc.yml new file mode 100644 index 00000000..69f74afd --- /dev/null +++ b/api-reference/0.0/toc.yml @@ -0,0 +1,42 @@ +### YamlMime:TableOfContent +- name: PolylineAlgorithm + href: PolylineAlgorithm.yml + items: + - name: Classes + - name: FormatterBuilder + href: PolylineAlgorithm.FormatterBuilder-2.yml + - name: InvalidPolylineException + href: PolylineAlgorithm.InvalidPolylineException.yml + - name: PolylineDecoder + href: PolylineAlgorithm.PolylineDecoder-2.yml + - name: PolylineDecodingOptions + href: PolylineAlgorithm.PolylineDecodingOptions-1.yml + - name: PolylineEncoder + href: PolylineAlgorithm.PolylineEncoder-2.yml + - name: PolylineEncoding + href: PolylineAlgorithm.PolylineEncoding.yml + - name: PolylineEncodingOptions + href: PolylineAlgorithm.PolylineEncodingOptions-1.yml + - name: PolylineFormatter + href: PolylineAlgorithm.PolylineFormatter-2.yml + - name: PolylineOptions + href: PolylineAlgorithm.PolylineOptions-2.yml + - name: Delegates + - name: PolylineItemFactory + href: PolylineAlgorithm.PolylineItemFactory-1.yml +- name: PolylineAlgorithm.Abstraction + href: PolylineAlgorithm.Abstraction.yml + items: + - name: Interfaces + - name: IPolylineDecoder + href: PolylineAlgorithm.Abstraction.IPolylineDecoder-2.yml + - name: IPolylineEncoder + href: PolylineAlgorithm.Abstraction.IPolylineEncoder-2.yml + - name: IPolylineFormatter + href: PolylineAlgorithm.Abstraction.IPolylineFormatter-2.yml +- name: PolylineAlgorithm.Extensions + href: PolylineAlgorithm.Extensions.yml + items: + - name: Classes + - name: PolylineEncoderExtensions + href: PolylineAlgorithm.Extensions.PolylineEncoderExtensions.yml