Skip to content

Commit 00ef315

Browse files
authored
Merge pull request #7 from JonMcPherson/2.0
Release Version 2.0 https://www.nuget.org/packages/OpenLocationCode/2.0.0
2 parents 1d26c31 + 588d78d commit 00ef315

14 files changed

Lines changed: 1087 additions & 696 deletions
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Google.OpenLocationCode;
4+
using NUnit.Framework;
5+
6+
public static class EncodingTest {
7+
8+
// Test cases for encoding latitude and longitude to codes and expected
9+
// https://github.com/google/open-location-code/blob/master/test_data/encodingTests.csv
10+
private static readonly List<TestData> TestDataList = new List<TestData> {
11+
new TestData("7FG49Q00+", "7FG49Q", 20.375, 2.775, 20.35, 2.75, 20.4, 2.8),
12+
new TestData("7FG49QCJ+2V", "7FG49QCJ2V", 20.3700625, 2.7821875, 20.37, 2.782125, 20.370125, 2.78225),
13+
new TestData("7FG49QCJ+2VX", "7FG49QCJ2VX", 20.3701125, 2.782234375, 20.3701, 2.78221875, 20.370125, 2.78225),
14+
new TestData("7FG49QCJ+2VXGJ", "7FG49QCJ2VXGJ", 20.3701135, 2.78223535156, 20.370113, 2.782234375, 20.370114, 2.78223632813),
15+
new TestData("8FVC2222+22", "8FVC222222", 47.0000625, 8.0000625, 47.0, 8.0, 47.000125, 8.000125),
16+
new TestData("4VCPPQGP+Q9", "4VCPPQGPQ9", -41.2730625, 174.7859375, -41.273125, 174.785875, -41.273, 174.786),
17+
new TestData("62G20000+", "62G2", 0.5, -179.5, 0.0, -180.0, 1, -179),
18+
new TestData("22220000+", "2222", -89.5, -179.5, -90, -180, -89, -179),
19+
new TestData("7FG40000+", "7FG4", 20.5, 2.5, 20.0, 2.0, 21.0, 3.0),
20+
new TestData("22222222+22", "2222222222", -89.9999375, -179.9999375, -90.0, -180.0, -89.999875, -179.999875),
21+
new TestData("6VGX0000+", "6VGX", 0.5, 179.5, 0, 179, 1, 180),
22+
new TestData("6FH32222+222", "6FH32222222", 1, 1, 1, 1, 1.000025, 1.00003125),
23+
// Special cases over 90 latitude and 180 longitude
24+
new TestData("CFX30000+", "CFX3", 90, 1, 89, 1, 90, 2),
25+
new TestData("CFX30000+", "CFX3", 92, 1, 89, 1, 90, 2),
26+
new TestData("62H20000+", "62H2", 1, 180, 1, -180, 2, -179),
27+
new TestData("62H30000+", "62H3", 1, 181, 1, -179, 2, -178),
28+
new TestData("CFX3X2X2+X2", "CFX3X2X2X2", 90, 1, 89.9998750, 1, 90, 1.0001250),
29+
// Test non-precise latitude/longitude value
30+
new TestData("6FH56C22+22", "6FH56C2222", 1.2, 3.4, 1.2000000000000028, 3.4000000000000057, 1.2001249999999999, 3.4001250000000027)
31+
};
32+
33+
public class TheEncodeMethod {
34+
[Test]
35+
public void ShouldEncodePointToExpectedLocationCode() {
36+
foreach (var testData in TestDataList) {
37+
Assert.AreEqual(testData.Code, OpenLocationCode.Encode(testData.EncodedLatitude, testData.EncodedLongitude, testData.CodeDigits.Length),
38+
$"Latitude {testData.EncodedLatitude} and longitude {testData.EncodedLongitude} were wrongly encoded.");
39+
}
40+
}
41+
42+
[Test]
43+
public void ShouldClipCoordinatesWhenExceedingMaximum() {
44+
Assert.AreEqual(OpenLocationCode.Encode(-90, 5), OpenLocationCode.Encode(-91, 5),
45+
"Clipping of negative latitude doesn't work.");
46+
Assert.AreEqual(OpenLocationCode.Encode(90, 5), OpenLocationCode.Encode(91, 5),
47+
"Clipping of positive latitude doesn't work.");
48+
Assert.AreEqual(OpenLocationCode.Encode(5, 175), OpenLocationCode.Encode(5, -185),
49+
"Clipping of negative longitude doesn't work.");
50+
Assert.AreEqual(OpenLocationCode.Encode(5, 175), OpenLocationCode.Encode(5, -905),
51+
"Clipping of very long negative longitude doesn't work.");
52+
Assert.AreEqual(OpenLocationCode.Encode(5, -175), OpenLocationCode.Encode(5, 905),
53+
"Clipping of very long positive longitude doesn't work.");
54+
}
55+
56+
[Test]
57+
public void ShouldLimitCodeLengthWhenExceedingMaximum() {
58+
string code = OpenLocationCode.Encode(51.3701125, -10.202665625, 1000000);
59+
60+
Assert.AreEqual(code.Length, OpenLocationCode.MaxCodeLength + 1,
61+
"Encoded code should have a length of MaxCodeLength + 1 for the plus symbol");
62+
}
63+
}
64+
65+
public class TheEncodeConstructor {
66+
[Test]
67+
public void ShouldEncodePointToExpectedLocationCode() {
68+
foreach (var testData in TestDataList) {
69+
OpenLocationCode olc = new OpenLocationCode(testData.EncodedLatitude, testData.EncodedLongitude, testData.CodeDigits.Length);
70+
Assert.AreEqual(testData.Code, olc.Code,
71+
$"Wrong code enocded for latitude {testData.EncodedLatitude} and longitude {testData.EncodedLongitude}.");
72+
}
73+
}
74+
[Test]
75+
public void ShouldTrimCodesIntoToExpectedCodeDigits() {
76+
foreach (var testData in TestDataList) {
77+
OpenLocationCode olc = new OpenLocationCode(testData.EncodedLatitude, testData.EncodedLongitude, testData.CodeDigits.Length);
78+
Assert.AreEqual(testData.CodeDigits, olc.CodeDigits,
79+
$"Wrong digits trimmed for encoded latitude {testData.EncodedLatitude} and longitude {testData.EncodedLongitude}.");
80+
}
81+
}
82+
}
83+
84+
85+
public class TheDecodeMethod {
86+
[Test]
87+
public void ShouldDecodeFullCodesToExpectedCodeArea() {
88+
foreach (var testData in TestDataList) {
89+
AssertExpectedDecodedArea(testData, OpenLocationCode.Decode(testData.Code));
90+
}
91+
}
92+
93+
[Test]
94+
public void ShouldDecodeFullCodesWithLowercaseCharactersToExpectedCodeArea() {
95+
foreach (var testData in TestDataList) {
96+
AssertExpectedDecodedArea(testData, OpenLocationCode.Decode(testData.Code.ToLower()));
97+
}
98+
}
99+
100+
[Test]
101+
public void ShouldDecodeToCodeAreaWithValidContainmentRelation() {
102+
foreach (var testData in TestDataList) {
103+
var olc = new OpenLocationCode(testData.Code);
104+
var decoded = olc.Decode();
105+
Assert.True(decoded.Contains(decoded.CenterLatitude, decoded.CenterLongitude),
106+
$"Containment relation is broken for the decoded middle point of code {testData.Code}");
107+
Assert.True(decoded.Contains(decoded.SouthLatitude, decoded.WestLongitude),
108+
$"Containment relation is broken for the decoded bottom left corner of code {testData.Code}");
109+
Assert.False(decoded.Contains(decoded.NorthLatitude, decoded.EastLongitude),
110+
$"Containment relation is broken for the decoded top right corner of code {testData.Code}");
111+
Assert.False(decoded.Contains(decoded.SouthLatitude, decoded.EastLongitude),
112+
$"Containment relation is broken for the decoded bottom right corner of code {testData.Code}");
113+
Assert.False(decoded.Contains(decoded.NorthLatitude, decoded.WestLongitude),
114+
$"Containment relation is broken for the decoded top left corner of code {testData.Code}");
115+
}
116+
}
117+
118+
[Test]
119+
public void ShouldDecodeToCodeAreaWithExpectedDimensions() {
120+
Assert.AreEqual(20.0, OpenLocationCode.Decode("67000000+").LongitudeWidth, 0);
121+
Assert.AreEqual(20.0, OpenLocationCode.Decode("67000000+").LatitudeHeight, 0);
122+
Assert.AreEqual(1.0, OpenLocationCode.Decode("67890000+").LongitudeWidth, 0);
123+
Assert.AreEqual(1.0, OpenLocationCode.Decode("67890000+").LatitudeHeight, 0);
124+
Assert.AreEqual(0.05, OpenLocationCode.Decode("6789CF00+").LongitudeWidth, 0);
125+
Assert.AreEqual(0.05, OpenLocationCode.Decode("6789CF00+").LatitudeHeight, 0);
126+
Assert.AreEqual(0.0025, OpenLocationCode.Decode("6789CFGH+").LongitudeWidth, 0);
127+
Assert.AreEqual(0.0025, OpenLocationCode.Decode("6789CFGH+").LatitudeHeight, 0);
128+
Assert.AreEqual(0.000125, OpenLocationCode.Decode("6789CFGH+JM").LongitudeWidth, 0);
129+
Assert.AreEqual(0.000125, OpenLocationCode.Decode("6789CFGH+JM").LatitudeHeight, 0);
130+
Assert.AreEqual(0.00003125, OpenLocationCode.Decode("6789CFGH+JMP").LongitudeWidth, 0);
131+
Assert.AreEqual(0.000025, OpenLocationCode.Decode("6789CFGH+JMP").LatitudeHeight, 0);
132+
}
133+
134+
[Test]
135+
public void ShouldThrowArgumentExceptionForInvalidOrShortCodes() {
136+
foreach (string code in new[] { null, "INVALID", "9QCJ+2VX" }) {
137+
Assert.Throws<ArgumentException>(() => OpenLocationCode.Decode(code),
138+
$"Expected exception was not thrown for code {code}");
139+
}
140+
}
141+
142+
}
143+
144+
public class TheDecodeConstructor {
145+
[Test]
146+
public void ShouldAcceptFullCodesAndDecodeToExpectedCodeArea() {
147+
foreach (var testData in TestDataList) {
148+
AssertExpectedDecodedArea(testData, new OpenLocationCode(testData.Code).Decode());
149+
}
150+
}
151+
152+
[Test]
153+
public void ShouldNormalizeFullCodeDigitsToExpectedCode() {
154+
foreach (var testData in TestDataList) {
155+
Assert.AreEqual(testData.Code, new OpenLocationCode(testData.CodeDigits).Code,
156+
$"Wrong code normalized from code digits {testData.CodeDigits}");
157+
}
158+
}
159+
160+
[Test]
161+
public void ShouldTrimCodesIntoToExpectedCodeDigits() {
162+
foreach (var testData in TestDataList) {
163+
Assert.AreEqual(testData.CodeDigits, new OpenLocationCode(testData.Code).CodeDigits,
164+
$"Wrong digits trimmed from code {testData.Code}.");
165+
}
166+
}
167+
168+
[Test]
169+
public void ShouldThrowArgumentExceptionForInvalidOrShortCodes() {
170+
foreach (string code in new[] { null, "INVALID", "9QCJ+2VX" }) {
171+
Assert.Throws<ArgumentException>(() => new OpenLocationCode(code),
172+
$"Expected exception was not thrown for code {code}");
173+
}
174+
}
175+
}
176+
177+
private static void AssertExpectedDecodedArea(TestData testData, CodeArea decodedArea) {
178+
Assert.True(IsNear(testData.DecodedArea.SouthLatitude, decodedArea.SouthLatitude),
179+
$"Wrong decoded low latitude for code {testData.Code}");
180+
Assert.True(IsNear(testData.DecodedArea.NorthLatitude, decodedArea.NorthLatitude),
181+
$"Wrong decoded high latitude for code {testData.Code}");
182+
Assert.True(IsNear(testData.DecodedArea.WestLongitude, decodedArea.WestLongitude),
183+
$"Wrong decoded low longitude for code {testData.Code}");
184+
Assert.True(IsNear(testData.DecodedArea.EastLongitude, decodedArea.EastLongitude),
185+
$"Wrong decoded high longitude for code {testData.Code}");
186+
}
187+
188+
private static bool IsNear(double a, double b) {
189+
return Math.Abs(a - b) < 1e-10;
190+
}
191+
192+
193+
private struct TestData {
194+
195+
internal TestData(string code, string codeDigits, double lat, double lon, double decodedMinLat, double decodedMinLon, double decodedMaxLat, double decodedMaxLon) {
196+
Code = code;
197+
CodeDigits = codeDigits;
198+
EncodedLatitude = lat;
199+
EncodedLongitude = lon;
200+
DecodedArea = new CodeArea(decodedMinLat, decodedMinLon, decodedMaxLat, decodedMaxLon);
201+
}
202+
203+
internal string Code { get; }
204+
internal string CodeDigits { get; }
205+
internal double EncodedLatitude { get; }
206+
internal double EncodedLongitude { get; }
207+
internal CodeArea DecodedArea { get; }
208+
209+
}
210+
}
Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
2-
3-
<PropertyGroup>
4-
<TargetFramework>netcoreapp2.0</TargetFramework>
5-
6-
<IsPackable>false</IsPackable>
7-
</PropertyGroup>
8-
9-
<ItemGroup>
10-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
11-
<PackageReference Include="NUnit" Version="3.11.0" />
12-
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
13-
</ItemGroup>
14-
15-
<ItemGroup>
16-
<ProjectReference Include="..\OpenLocationCode\OpenLocationCode.csproj" />
17-
</ItemGroup>
18-
</Project>
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp2.0</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
11+
<PackageReference Include="NUnit" Version="3.11.0" />
12+
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\OpenLocationCode\OpenLocationCode.csproj" />
17+
</ItemGroup>
18+
</Project>
Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using Google.OpenLocationCode;
34
using NUnit.Framework;
45

56
public static class ShorteningTest {
67

8+
// Test cases for shortening full codes and recovering short codes.
9+
// test_type is R for recovery only, S for shorten only, or B for both.
10+
// See: https://github.com/google/open-location-code/blob/master/test_data/shortCodeTests.csv
711
private static readonly List<TestData> TestDataList = new List<TestData> {
812
new TestData("9C3W9QCJ+2VX", 51.3701125, -1.217765625, "+2VX", "B"),
913
// Adjust so we can't trim by 8 (+/- .000755)
@@ -35,41 +39,60 @@ public static class ShorteningTest {
3539

3640
public class TheShortenMethod {
3741
[Test]
38-
public void ShouldShortenLongCodeToShortCodeFromReferencePoint() {
42+
public void ShouldShortenFullCodeToShortCodeFromReferencePoint() {
3943
foreach (var testData in TestDataList) {
4044
if (testData.TestType != "B" && testData.TestType != "S") {
4145
continue;
4246
}
43-
OpenLocationCode olc = new OpenLocationCode(testData.Code);
44-
OpenLocationCode shortened = olc.Shorten(testData.ReferenceLatitude, testData.ReferenceLongitude);
45-
Assert.AreEqual(testData.ShortCode, shortened.Code);
47+
48+
OpenLocationCode.ShortCode shortened = OpenLocationCode.Shorten(testData.Code,
49+
testData.ReferenceLatitude, testData.ReferenceLongitude);
50+
Assert.AreEqual(testData.ShortCode, shortened.Code,
51+
$"Wrong shortening of code {testData.Code} from reference latitude {testData.ReferenceLatitude} and longitude {testData.ReferenceLongitude}.");
52+
}
53+
}
54+
55+
[Test]
56+
public void ShouldThrowArgumentExceptionForInvalidOrShortOrPaddedCodes() {
57+
foreach (string code in new[] { null, "INVALID", "2222+22", "9C3W9Q00+" }) {
58+
Assert.Throws<ArgumentException>(() => OpenLocationCode.Shorten(code, 0, 0),
59+
$"Expected exception was not thrown for code {code}");
4660
}
4761
}
4862
}
4963

50-
public class TheRecoverMethod {
64+
public class TheRecoverNearestMethod {
5165
[Test]
5266
public void ShouldRecoverShortCodeToLongCodeFromReferencePoint() {
5367
foreach (var testData in TestDataList) {
5468
if (testData.TestType != "B" && testData.TestType != "R") {
5569
continue;
5670
}
57-
OpenLocationCode olc = new OpenLocationCode(testData.ShortCode);
58-
OpenLocationCode recovered = olc.Recover(testData.ReferenceLatitude, testData.ReferenceLongitude);
59-
Assert.AreEqual(testData.Code, recovered.Code);
71+
OpenLocationCode recovered = OpenLocationCode.ShortCode.RecoverNearest(testData.ShortCode,
72+
testData.ReferenceLatitude, testData.ReferenceLongitude);
73+
Assert.AreEqual(testData.Code, recovered.Code,
74+
$"Wrong recovery of short code {testData.ShortCode} from reference latitude {testData.ReferenceLatitude} and longitude {testData.ReferenceLongitude}.");
6075
}
6176
}
6277

6378
[Test]
6479
public void ShouldRecoverShortCodesNearSouthPole() {
65-
OpenLocationCode olc = new OpenLocationCode("XXXXXX+XX");
66-
Assert.AreEqual("2CXXXXXX+XX", olc.Recover(-81.0, 0.0).Code);
80+
Assert.AreEqual("2CXXXXXX+XX", OpenLocationCode.ShortCode.RecoverNearest("XXXXXX+XX", - 81.0, 0.0).Code);
6781
}
6882

6983
[Test]
7084
public void ShouldRecoverShortCodesNearNorthPole() {
71-
OpenLocationCode olc = new OpenLocationCode("2222+22");
72-
Assert.AreEqual("CFX22222+22", olc.Recover(89.6, 0.0).Code);
85+
Assert.AreEqual("CFX22222+22", OpenLocationCode.ShortCode.RecoverNearest("2222+22", 89.6, 0.0).Code);
86+
}
87+
}
88+
89+
public class TheShortCodeConstructor {
90+
[Test]
91+
public void ShouldThrowArgumentExceptionForInvalidShortCodes() {
92+
foreach (string code in new []{ null, "INVALID", "3W9Q00+", "9C3W9Q00+", "9C3W9QCJ+2VX" }) {
93+
Assert.Throws<ArgumentException>(() => new OpenLocationCode.ShortCode(code),
94+
$"Expected exception not thrown for code {code}");
95+
}
7396
}
7497
}
7598

0 commit comments

Comments
 (0)