Skip to content

Commit 09386c1

Browse files
author
Petr Sramek
committed
Refactored V2 implementation
1 parent 7f04f85 commit 09386c1

File tree

14 files changed

+312
-367
lines changed

14 files changed

+312
-367
lines changed

benchmarks/DropoutCoder.PolylineAlgorithm.Benchmarks/PolylineEncodingBenchmark.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace DropoutCoder.PolylineAlgorithm.Benchmarks
77
{
88
using BenchmarkDotNet.Attributes;
99
using BenchmarkDotNet.Engines;
10-
using DropoutCoder.PolylineAlgorithm;
10+
using DropoutCoder.PolylineAlgorithm.Encoding;
1111

1212
[MemoryDiagnoser]
1313
[MarkdownExporter]
@@ -20,16 +20,16 @@ public class PolylineEncodingBenchmark
2020

2121
public IEnumerable<(double, double)> Coordinates;
2222

23-
internal PolylineEncoder Encoder { get; private set; }
23+
internal DefaultPolylineEncoding Encoder { get; private set; }
2424

25-
public char[] Polyline;
25+
public string Polyline;
2626

2727
[GlobalSetup]
2828
public void Setup()
2929
{
30-
Encoder = new PolylineEncoder();
30+
Encoder = new DefaultPolylineEncoding();
3131
Coordinates = new[] { (42.88895, -100.30630), (44.91513, 19.22495), (20.40244, 7.97495), (-15.52130, -63.74380), (-78.95116, -72.18130), (38.63072, 88.13120), (60.81071, 151.41245), (-58.20769, -173.43130), (59.40939, 83.91245), (-58.20769, 61.41245), (-20.86278, -119.99380), (34.10374, -150.93130), (-71.15367, 31.88120), (-72.04138, -153.74380), (-49.99635, -107.33755), (76.12614, 135.94370), (70.05664, 41.72495), (63.43879, -77.80630), (13.68456, -90.46255), (-75.90519, -7.49380), (74.71112, -127.02505), (-66.61109, 17.81870), (-49.08384, 37.50620) };
32-
Polyline = "}vwdGjafcRsvjKi}pxUhsrtCngtcAjjgzEdqvtLrscbKj}nr@wetlUc`nq]}_kfCyrfaK~wluUl`u}|@wa{lUmmuap@va{lU~oihCu||bF`|era@wsnnIjny{DxamaScqxza@dklDf{}kb@mtpeCavfzGqhx`Wyzzkm@jm`d@dba~Pppkg@h}pxU|rtnHp|flA|~xaPuykyN}fhv[h}pxUx~p}Ymx`sZih~iB{edwB".ToCharArray();
32+
Polyline = "}vwdGjafcRsvjKi}pxUhsrtCngtcAjjgzEdqvtLrscbKj}nr@wetlUc`nq]}_kfCyrfaK~wluUl`u}|@wa{lUmmuap@va{lU~oihCu||bF`|era@wsnnIjny{DxamaScqxza@dklDf{}kb@mtpeCavfzGqhx`Wyzzkm@jm`d@dba~Pppkg@h}pxU|rtnHp|flA|~xaPuykyN}fhv[h}pxUx~p}Ymx`sZih~iB{edwB";
3333
}
3434

3535
[Benchmark]

benchmarks/DropoutCoder.PolylineAlgorithm.Implementation.Benchmarks/DecodePerformanceBenchmark.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
{
33
using BenchmarkDotNet.Attributes;
44
using BenchmarkDotNet.Engines;
5-
using DropoutCoder.PolylineAlgorithm.Validation;
65
using System;
6+
using System.Runtime.CompilerServices;
77

88
[MemoryDiagnoser]
99
public class DecodePerformanceBenchmark
@@ -279,6 +279,39 @@ double GetCoordinate(int value)
279279
#endregion
280280
}
281281
}
282+
283+
/// <summary>
284+
/// Performs coordinate validation
285+
/// </summary>
286+
internal static class CoordinateValidator
287+
{
288+
#region Methods
289+
290+
/// <summary>
291+
/// Performs coordinate validation
292+
/// </summary>
293+
/// <param name="coordinate">Coordinate to validate</param>
294+
/// <returns>Returns validation result. If valid then true, otherwise false.</returns>
295+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
296+
public static bool IsValid((double Latitude, double Longitude) coordinate)
297+
{
298+
return IsValidLatitude(coordinate.Latitude) && IsValidLongitude(coordinate.Longitude);
299+
}
300+
301+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
302+
public static bool IsValidLatitude(double latitude)
303+
{
304+
return latitude >= Constants.Coordinate.MinLatitude && latitude <= Constants.Coordinate.MaxLatitude;
305+
}
306+
307+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
308+
public static bool IsValidLongitude(double longitude)
309+
{
310+
return longitude >= Constants.Coordinate.MinLongitude && longitude <= Constants.Coordinate.MaxLongitude;
311+
}
312+
313+
#endregion
314+
}
282315
}
283316

284317
internal class For

benchmarks/DropoutCoder.PolylineAlgorithm.Implementation.Benchmarks/EncodePerformanceBenchmark.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
{
33
using BenchmarkDotNet.Attributes;
44
using BenchmarkDotNet.Engines;
5-
using DropoutCoder.PolylineAlgorithm.Validation;
65
using Microsoft.Extensions.ObjectPool;
76
using System.Collections.Generic;
87
using System.Text;
@@ -345,6 +344,16 @@ public static bool IsValidLongitude(double longitude)
345344
return longitude >= Constants.Coordinate.MinLongitude && longitude <= Constants.Coordinate.MaxLongitude;
346345
}
347346
}
347+
348+
public class CoordinateValidationException : Exception
349+
{
350+
public CoordinateValidationException(double latitude, double longitude)
351+
: base(string.Format("Latitude {0} or longitude {1} is not valid. Latitude must be in range between -90 and +90. Longitude must be in range between -180 and +180.", latitude, longitude)) { }
352+
353+
public double Latitude { get; }
354+
355+
public double Longitude { get; }
356+
}
348357
}
349358

350359
internal class For

src/DropoutCoder.PolylineAlgorithm.DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,33 @@
55

66
namespace DropoutCoder.PolylineAlgorithm.DependencyInjection
77
{
8+
using DropoutCoder.PolylineAlgorithm.Encoding;
89
using Microsoft.Extensions.DependencyInjection;
910

1011
public static class ServiceCollectionExtensions
1112
{
13+
/// <summary>
14+
/// Registers singleton instance of <seealso cref="DefaultPolylineEncoding" /> to dependency container.
15+
/// </summary>
16+
/// <param name="services">Instance of <seealso cref="IServiceCollection"/></param>
17+
/// <returns>nstance of <seealso cref="IServiceCollection"/></returns>
1218
public static IServiceCollection AddPolylineEncoder(this IServiceCollection services)
1319
{
1420
return services
15-
.AddSingleton<IPolylineEncoder, PolylineEncoder>();
21+
.AddSingleton<DefaultPolylineEncoding, DefaultPolylineEncoding>();
22+
}
23+
24+
/// <summary>
25+
/// Registers singleton instance of <seealso cref="TImplementation" /> to dependency container.
26+
/// </summary>
27+
/// <param name="services">Instance of <seealso cref="IServiceCollection"/></param>
28+
/// <returns>nstance of <seealso cref="IServiceCollection"/></returns>
29+
public static IServiceCollection AddPolylineEncoder<TService, TImplementation>(this IServiceCollection services)
30+
where TService : class
31+
where TImplementation : class, TService
32+
{
33+
return services
34+
.AddSingleton<TService, TImplementation>();
1635
}
1736
}
1837
}

src/DropoutCoder.PolylineAlgorithm/PolylineEncoder.cs renamed to src/DropoutCoder.PolylineAlgorithm/Encoding/DefaultPolylineEncoding.cs

Lines changed: 64 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,27 @@
33
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
44
//
55

6-
namespace DropoutCoder.PolylineAlgorithm
6+
using DropoutCoder.PolylineAlgorithm.Internal;
7+
8+
namespace DropoutCoder.PolylineAlgorithm.Encoding
79
{
8-
using DropoutCoder.PolylineAlgorithm.Internal;
910
using System;
1011
using System.Collections.Generic;
1112
using System.Linq;
1213
using System.Runtime.CompilerServices;
1314
using System.Text;
1415

15-
public sealed class PolylineEncoder : IPolylineEncoder
16+
public sealed class DefaultPolylineEncoding : IPolylineEncoding<(double Latitude, double Longitude)>
1617
{
1718
private readonly CoordinateValidator _validator = new CoordinateValidator();
1819

1920
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20-
public IEnumerable<(double Latitude, double Longitude)> Decode(char[] polyline)
21+
public IEnumerable<(double Latitude, double Longitude)> Decode(string source)
2122
{
2223
// Checking null and at least one character
23-
if (polyline == null || polyline.Length == 0)
24+
if (source == null || source.Length == 0)
2425
{
25-
throw new ArgumentException(ExceptionMessageResource.ArgumentCannotBeNullOrEmpty, nameof(polyline));
26+
throw new ArgumentException(ExceptionMessageResource.ArgumentCannotBeNullOrEmpty, nameof(source));
2627
}
2728

2829
// Initialize local variables
@@ -31,16 +32,16 @@ public sealed class PolylineEncoder : IPolylineEncoder
3132
int longitude = 0;
3233

3334
// Looping through encoded polyline char array
34-
while (index < polyline.Length)
35+
while (index < source.Length)
3536
{
3637
// Attempting to calculate next latitude value. If failed exception is thrown
37-
if (!TryCalculateNext(polyline, ref index, ref latitude))
38+
if (!TryCalculateNext(ref source, ref index, ref latitude))
3839
{
3940
throw new InvalidOperationException(ExceptionMessageResource.PolylineCharArrayIsMalformed);
4041
}
4142

4243
// Attempting to calculate next longitude value. If failed exception is thrown
43-
if (!TryCalculateNext(polyline, ref index, ref longitude))
44+
if (!TryCalculateNext(ref source, ref index, ref longitude))
4445
{
4546
throw new InvalidOperationException(ExceptionMessageResource.PolylineCharArrayIsMalformed);
4647
}
@@ -54,37 +55,6 @@ public sealed class PolylineEncoder : IPolylineEncoder
5455
}
5556

5657
yield return coordinate;
57-
58-
#region Local functions
59-
60-
bool TryCalculateNext(char[] polyline, ref int index, ref int value)
61-
{
62-
// Local variable initialization
63-
int chunk;
64-
int sum = 0;
65-
int shifter = 0;
66-
67-
do
68-
{
69-
chunk = polyline[index++] - Constants.ASCII.QuestionMark;
70-
sum |= (chunk & Constants.ASCII.UnitSeparator) << shifter;
71-
shifter += Constants.ShiftLength;
72-
} while (chunk >= Constants.ASCII.Space && index < polyline.Length);
73-
74-
if (index >= polyline.Length && chunk >= Constants.ASCII.Space)
75-
return false;
76-
77-
value += (sum & 1) == 1 ? ~(sum >> 1) : sum >> 1;
78-
79-
return true;
80-
}
81-
82-
double GetCoordinate(int value)
83-
{
84-
return Convert.ToDouble(value) / Constants.Precision;
85-
}
86-
87-
#endregion
8858
}
8959
}
9060

@@ -121,48 +91,75 @@ public string Encode(IEnumerable<(double Latitude, double Longitude)> coordinate
12191
}
12292

12393
return sb.ToString();
94+
}
12495

125-
#region Local functions
96+
#region Private methods
12697

127-
bool TryValidate(IEnumerable<(double Latitude, double Longitude)> collection, out ICollection<CoordinateValidationException> exceptions)
98+
bool TryCalculateNext(ref string polyline, ref int index, ref int value)
99+
{
100+
// Local variable initialization
101+
int chunk;
102+
int sum = 0;
103+
int shifter = 0;
104+
105+
do
128106
{
129-
exceptions = new List<CoordinateValidationException>(collection.Count());
107+
chunk = polyline[index++] - Constants.ASCII.QuestionMark;
108+
sum |= (chunk & Constants.ASCII.UnitSeparator) << shifter;
109+
shifter += Constants.ShiftLength;
110+
} while (chunk >= Constants.ASCII.Space && index < polyline.Length);
130111

131-
foreach (var item in collection)
132-
{
133-
if (!_validator.IsValid(item))
134-
{
135-
exceptions.Add(new CoordinateValidationException(item.Latitude, item.Longitude));
136-
}
137-
}
112+
if (index >= polyline.Length && chunk >= Constants.ASCII.Space)
113+
return false;
138114

139-
return !exceptions.GetEnumerator().MoveNext();
140-
}
115+
value += (sum & 1) == 1 ? ~(sum >> 1) : sum >> 1;
141116

142-
int Round(double value)
117+
return true;
118+
}
119+
120+
double GetCoordinate(int value)
121+
{
122+
return Convert.ToDouble(value) / Constants.Precision;
123+
}
124+
125+
bool TryValidate(IEnumerable<(double Latitude, double Longitude)> collection, out ICollection<CoordinateValidationException> exceptions)
126+
{
127+
exceptions = new List<CoordinateValidationException>(collection.Count());
128+
129+
foreach (var item in collection)
143130
{
144-
return (int)Math.Round(value * Constants.Precision);
131+
if (!_validator.IsValid(item))
132+
{
133+
exceptions.Add(new CoordinateValidationException(item.Latitude, item.Longitude));
134+
}
145135
}
146136

147-
IEnumerable<char> GetSequence(int value)
148-
{
149-
int shifted = value << 1;
150-
if (value < 0)
151-
shifted = ~shifted;
137+
return !exceptions.GetEnumerator().MoveNext();
138+
}
152139

153-
int rem = shifted;
140+
int Round(double value)
141+
{
142+
return (int)Math.Round(value * Constants.Precision);
143+
}
154144

155-
while (rem >= Constants.ASCII.Space)
156-
{
157-
yield return (char)((Constants.ASCII.Space | rem & Constants.ASCII.UnitSeparator) + Constants.ASCII.QuestionMark);
145+
IEnumerable<char> GetSequence(int value)
146+
{
147+
int shifted = value << 1;
148+
if (value < 0)
149+
shifted = ~shifted;
158150

159-
rem >>= Constants.ShiftLength;
160-
}
151+
int rem = shifted;
152+
153+
while (rem >= Constants.ASCII.Space)
154+
{
155+
yield return (char)((Constants.ASCII.Space | rem & Constants.ASCII.UnitSeparator) + Constants.ASCII.QuestionMark);
161156

162-
yield return (char)(rem + Constants.ASCII.QuestionMark);
157+
rem >>= Constants.ShiftLength;
163158
}
164159

165-
#endregion
160+
yield return (char)(rem + Constants.ASCII.QuestionMark);
166161
}
162+
163+
#endregion
167164
}
168165
}

src/Encoding/PolylineEncodingBase.cs renamed to src/DropoutCoder.PolylineAlgorithm/Encoding/PolylineEncoding`1.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace DropoutCoder.PolylineAlgorithm.Encoding
1313
/// Defines base class for all polyline encodings
1414
/// </summary>
1515
/// <typeparam name="T"></typeparam>
16-
public abstract class PolylineEncodingBase<T> : IPolylineEncoding<T>
16+
public abstract class PolylineEncoding<T> : IPolylineEncoding<T>
1717
{
1818
/// <summary>
1919
/// Method performs decode operation and coversion to desired type

src/DropoutCoder.PolylineAlgorithm/Internal/CoordinateValidator.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
namespace DropoutCoder.PolylineAlgorithm.Internal
22
{
3-
using System;
43
using System.Runtime.CompilerServices;
54

65

0 commit comments

Comments
 (0)