Skip to content

Commit

Permalink
Merge pull request #9 from JonMcPherson/float-handling
Browse files Browse the repository at this point in the history
Fix floating point precision issues with new algorithm for Encode() and Decode()
JonMcPherson authored Jul 6, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 00ef315 + a780eea commit a2bcf65
Showing 15 changed files with 679 additions and 455 deletions.
127 changes: 127 additions & 0 deletions OpenLocationCode.Test/DecodingTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using Google.OpenLocationCode;
using NUnit.Framework;

public static class DecodingTest {

private const double Precision = 1e-10;

// Test cases for decoding valid codes into code areas
// https://github.com/google/open-location-code/blob/master/test_data/decoding.csv
private static readonly IEnumerable<TestData> DecodingTestCases = TestDataUtils.ReadTestData<TestData>("decoding.csv");


public class TheDecodeMethod {
[Test]
public void ShouldDecodeFullCodesToExpectedCodeArea() {
foreach (var testData in DecodingTestCases) {
AssertExpectedDecodedArea(testData, OpenLocationCode.Decode(testData.Code));
}
}

[Test]
public void ShouldDecodeFullCodesWithLowercaseCharactersToExpectedCodeArea() {
foreach (var testData in DecodingTestCases) {
AssertExpectedDecodedArea(testData, OpenLocationCode.Decode(testData.Code.ToLower()));
}
}

[Test]
public void ShouldDecodeToCodeAreaWithValidContainmentRelation() {
foreach (var testData in DecodingTestCases) {
var olc = new OpenLocationCode(testData.Code);
var decoded = olc.Decode();
Assert.True(decoded.Contains(decoded.CenterLatitude, decoded.CenterLongitude),
$"Containment relation is broken for the decoded middle point of code {testData.Code}");
Assert.True(decoded.Contains(decoded.SouthLatitude, decoded.WestLongitude),
$"Containment relation is broken for the decoded bottom left corner of code {testData.Code}");
Assert.False(decoded.Contains(decoded.NorthLatitude, decoded.EastLongitude),
$"Containment relation is broken for the decoded top right corner of code {testData.Code}");
Assert.False(decoded.Contains(decoded.SouthLatitude, decoded.EastLongitude),
$"Containment relation is broken for the decoded bottom right corner of code {testData.Code}");
Assert.False(decoded.Contains(decoded.NorthLatitude, decoded.WestLongitude),
$"Containment relation is broken for the decoded top left corner of code {testData.Code}");
}
}

[Test]
public void ShouldDecodeToCodeAreaWithExpectedDimensions() {
Assert.AreEqual(20.0, OpenLocationCode.Decode("67000000+").LongitudeWidth, 0);
Assert.AreEqual(20.0, OpenLocationCode.Decode("67000000+").LatitudeHeight, 0);
Assert.AreEqual(1.0, OpenLocationCode.Decode("67890000+").LongitudeWidth, 0);
Assert.AreEqual(1.0, OpenLocationCode.Decode("67890000+").LatitudeHeight, 0);
Assert.AreEqual(0.05, OpenLocationCode.Decode("6789CF00+").LongitudeWidth, 0);
Assert.AreEqual(0.05, OpenLocationCode.Decode("6789CF00+").LatitudeHeight, 0);
Assert.AreEqual(0.0025, OpenLocationCode.Decode("6789CFGH+").LongitudeWidth, 0);
Assert.AreEqual(0.0025, OpenLocationCode.Decode("6789CFGH+").LatitudeHeight, 0);
Assert.AreEqual(0.000125, OpenLocationCode.Decode("6789CFGH+JM").LongitudeWidth, 0);
Assert.AreEqual(0.000125, OpenLocationCode.Decode("6789CFGH+JM").LatitudeHeight, 0);
Assert.AreEqual(0.00003125, OpenLocationCode.Decode("6789CFGH+JMP").LongitudeWidth, 0);
Assert.AreEqual(0.000025, OpenLocationCode.Decode("6789CFGH+JMP").LatitudeHeight, 0);
}

[Test]
public void ShouldThrowArgumentExceptionForInvalidOrShortCodes() {
foreach (string code in new[] { null, "INVALID", "9QCJ+2VX" }) {
Assert.Throws<ArgumentException>(() => OpenLocationCode.Decode(code),
$"Expected exception was not thrown for code {code}");
}
}

}

public class TheDecodeConstructor {
[Test]
public void ShouldAcceptFullCodesAndDecodeToExpectedCodeArea() {
foreach (var testData in DecodingTestCases) {
AssertExpectedDecodedArea(testData, new OpenLocationCode(testData.Code).Decode());
}
}

[Test]
public void ShouldNormalizeFullCodeDigitsToExpectedCode() {
foreach (var testData in DecodingTestCases) {
string codeDigits = OpenLocationCode.TrimCode(testData.Code);
Assert.AreEqual(testData.Code, new OpenLocationCode(codeDigits).Code,
$"Wrong code normalized from code digits {codeDigits}");
}
}

[Test]
public void ShouldTrimCodesIntoToExpectedCodeDigits() {
foreach (var testData in DecodingTestCases) {
Assert.AreEqual(OpenLocationCode.TrimCode(testData.Code), new OpenLocationCode(testData.Code).CodeDigits,
$"Wrong digits trimmed from code {testData.Code}.");
}
}

[Test]
public void ShouldThrowArgumentExceptionForInvalidOrShortCodes() {
foreach (string code in new[] { null, "INVALID", "9QCJ+2VX" }) {
Assert.Throws<ArgumentException>(() => new OpenLocationCode(code),
$"Expected exception was not thrown for code {code}");
}
}
}

private static void AssertExpectedDecodedArea(TestData testData, CodeArea decoded) {
Assert.AreEqual(testData.Length, decoded.CodeLength, $"Wrong length for code {testData.Code}");
Assert.AreEqual(testData.LatLo, decoded.SouthLatitude, Precision, $"Wrong low latitude for code {testData.Code}");
Assert.AreEqual(testData.LatHi, decoded.NorthLatitude, Precision, $"Wrong high latitude for code {testData.Code}");
Assert.AreEqual(testData.LngLo, decoded.WestLongitude, Precision, $"Wrong low longitude for code {testData.Code}");
Assert.AreEqual(testData.LngHi, decoded.EastLongitude, Precision, $"Wrong high longitude for code {testData.Code}");
}


public class TestData {

public string Code { get; set; }
public int Length { get; set; }
public double LatLo { get; set; }
public double LngLo { get; set; }
public double LatHi { get; set; }
public double LngHi { get; set; }

}
}
183 changes: 24 additions & 159 deletions OpenLocationCode.Test/EncodingTest.cs
Original file line number Diff line number Diff line change
@@ -5,37 +5,17 @@

public static class EncodingTest {

// Test cases for encoding latitude and longitude to codes and expected
// https://github.com/google/open-location-code/blob/master/test_data/encodingTests.csv
private static readonly List<TestData> TestDataList = new List<TestData> {
new TestData("7FG49Q00+", "7FG49Q", 20.375, 2.775, 20.35, 2.75, 20.4, 2.8),
new TestData("7FG49QCJ+2V", "7FG49QCJ2V", 20.3700625, 2.7821875, 20.37, 2.782125, 20.370125, 2.78225),
new TestData("7FG49QCJ+2VX", "7FG49QCJ2VX", 20.3701125, 2.782234375, 20.3701, 2.78221875, 20.370125, 2.78225),
new TestData("7FG49QCJ+2VXGJ", "7FG49QCJ2VXGJ", 20.3701135, 2.78223535156, 20.370113, 2.782234375, 20.370114, 2.78223632813),
new TestData("8FVC2222+22", "8FVC222222", 47.0000625, 8.0000625, 47.0, 8.0, 47.000125, 8.000125),
new TestData("4VCPPQGP+Q9", "4VCPPQGPQ9", -41.2730625, 174.7859375, -41.273125, 174.785875, -41.273, 174.786),
new TestData("62G20000+", "62G2", 0.5, -179.5, 0.0, -180.0, 1, -179),
new TestData("22220000+", "2222", -89.5, -179.5, -90, -180, -89, -179),
new TestData("7FG40000+", "7FG4", 20.5, 2.5, 20.0, 2.0, 21.0, 3.0),
new TestData("22222222+22", "2222222222", -89.9999375, -179.9999375, -90.0, -180.0, -89.999875, -179.999875),
new TestData("6VGX0000+", "6VGX", 0.5, 179.5, 0, 179, 1, 180),
new TestData("6FH32222+222", "6FH32222222", 1, 1, 1, 1, 1.000025, 1.00003125),
// Special cases over 90 latitude and 180 longitude
new TestData("CFX30000+", "CFX3", 90, 1, 89, 1, 90, 2),
new TestData("CFX30000+", "CFX3", 92, 1, 89, 1, 90, 2),
new TestData("62H20000+", "62H2", 1, 180, 1, -180, 2, -179),
new TestData("62H30000+", "62H3", 1, 181, 1, -179, 2, -178),
new TestData("CFX3X2X2+X2", "CFX3X2X2X2", 90, 1, 89.9998750, 1, 90, 1.0001250),
// Test non-precise latitude/longitude value
new TestData("6FH56C22+22", "6FH56C2222", 1.2, 3.4, 1.2000000000000028, 3.4000000000000057, 1.2001249999999999, 3.4001250000000027)
};
// Test cases for encoding latitude and longitude to codes
// https://github.com/google/open-location-code/blob/master/test_data/encoding.csv
private static readonly IEnumerable<TestData> EncodingTestCases = TestDataUtils.ReadTestData<TestData>("encoding.csv");


public class TheEncodeMethod {
[Test]
public void ShouldEncodePointToExpectedLocationCode() {
foreach (var testData in TestDataList) {
Assert.AreEqual(testData.Code, OpenLocationCode.Encode(testData.EncodedLatitude, testData.EncodedLongitude, testData.CodeDigits.Length),
$"Latitude {testData.EncodedLatitude} and longitude {testData.EncodedLongitude} were wrongly encoded.");
foreach (var testData in EncodingTestCases) {
Assert.AreEqual(testData.Code, OpenLocationCode.Encode(testData.Latitude, testData.Longitude, testData.Length),
$"Latitude {testData.Latitude} and longitude {testData.Longitude} were wrongly encoded.");
}
}

@@ -57,154 +37,39 @@ public void ShouldClipCoordinatesWhenExceedingMaximum() {
public void ShouldLimitCodeLengthWhenExceedingMaximum() {
string code = OpenLocationCode.Encode(51.3701125, -10.202665625, 1000000);

Assert.AreEqual(code.Length, OpenLocationCode.MaxCodeLength + 1,
"Encoded code should have a length of MaxCodeLength + 1 for the plus symbol");
Assert.AreEqual(code.Length, 16,
"Encoded code should have a length of 16 (15 + 1 for the plus symbol)");
}
}

public class TheEncodeConstructor {
[Test]
public void ShouldEncodePointToExpectedLocationCode() {
foreach (var testData in TestDataList) {
OpenLocationCode olc = new OpenLocationCode(testData.EncodedLatitude, testData.EncodedLongitude, testData.CodeDigits.Length);
foreach (var testData in EncodingTestCases) {
OpenLocationCode olc = new OpenLocationCode(testData.Latitude, testData.Longitude, testData.Length);
Assert.AreEqual(testData.Code, olc.Code,
$"Wrong code enocded for latitude {testData.EncodedLatitude} and longitude {testData.EncodedLongitude}.");
}
}
[Test]
public void ShouldTrimCodesIntoToExpectedCodeDigits() {
foreach (var testData in TestDataList) {
OpenLocationCode olc = new OpenLocationCode(testData.EncodedLatitude, testData.EncodedLongitude, testData.CodeDigits.Length);
Assert.AreEqual(testData.CodeDigits, olc.CodeDigits,
$"Wrong digits trimmed for encoded latitude {testData.EncodedLatitude} and longitude {testData.EncodedLongitude}.");
}
}
}


public class TheDecodeMethod {
[Test]
public void ShouldDecodeFullCodesToExpectedCodeArea() {
foreach (var testData in TestDataList) {
AssertExpectedDecodedArea(testData, OpenLocationCode.Decode(testData.Code));
}
}

[Test]
public void ShouldDecodeFullCodesWithLowercaseCharactersToExpectedCodeArea() {
foreach (var testData in TestDataList) {
AssertExpectedDecodedArea(testData, OpenLocationCode.Decode(testData.Code.ToLower()));
}
}

[Test]
public void ShouldDecodeToCodeAreaWithValidContainmentRelation() {
foreach (var testData in TestDataList) {
var olc = new OpenLocationCode(testData.Code);
var decoded = olc.Decode();
Assert.True(decoded.Contains(decoded.CenterLatitude, decoded.CenterLongitude),
$"Containment relation is broken for the decoded middle point of code {testData.Code}");
Assert.True(decoded.Contains(decoded.SouthLatitude, decoded.WestLongitude),
$"Containment relation is broken for the decoded bottom left corner of code {testData.Code}");
Assert.False(decoded.Contains(decoded.NorthLatitude, decoded.EastLongitude),
$"Containment relation is broken for the decoded top right corner of code {testData.Code}");
Assert.False(decoded.Contains(decoded.SouthLatitude, decoded.EastLongitude),
$"Containment relation is broken for the decoded bottom right corner of code {testData.Code}");
Assert.False(decoded.Contains(decoded.NorthLatitude, decoded.WestLongitude),
$"Containment relation is broken for the decoded top left corner of code {testData.Code}");
}
}

[Test]
public void ShouldDecodeToCodeAreaWithExpectedDimensions() {
Assert.AreEqual(20.0, OpenLocationCode.Decode("67000000+").LongitudeWidth, 0);
Assert.AreEqual(20.0, OpenLocationCode.Decode("67000000+").LatitudeHeight, 0);
Assert.AreEqual(1.0, OpenLocationCode.Decode("67890000+").LongitudeWidth, 0);
Assert.AreEqual(1.0, OpenLocationCode.Decode("67890000+").LatitudeHeight, 0);
Assert.AreEqual(0.05, OpenLocationCode.Decode("6789CF00+").LongitudeWidth, 0);
Assert.AreEqual(0.05, OpenLocationCode.Decode("6789CF00+").LatitudeHeight, 0);
Assert.AreEqual(0.0025, OpenLocationCode.Decode("6789CFGH+").LongitudeWidth, 0);
Assert.AreEqual(0.0025, OpenLocationCode.Decode("6789CFGH+").LatitudeHeight, 0);
Assert.AreEqual(0.000125, OpenLocationCode.Decode("6789CFGH+JM").LongitudeWidth, 0);
Assert.AreEqual(0.000125, OpenLocationCode.Decode("6789CFGH+JM").LatitudeHeight, 0);
Assert.AreEqual(0.00003125, OpenLocationCode.Decode("6789CFGH+JMP").LongitudeWidth, 0);
Assert.AreEqual(0.000025, OpenLocationCode.Decode("6789CFGH+JMP").LatitudeHeight, 0);
}

[Test]
public void ShouldThrowArgumentExceptionForInvalidOrShortCodes() {
foreach (string code in new[] { null, "INVALID", "9QCJ+2VX" }) {
Assert.Throws<ArgumentException>(() => OpenLocationCode.Decode(code),
$"Expected exception was not thrown for code {code}");
}
}

}

public class TheDecodeConstructor {
[Test]
public void ShouldAcceptFullCodesAndDecodeToExpectedCodeArea() {
foreach (var testData in TestDataList) {
AssertExpectedDecodedArea(testData, new OpenLocationCode(testData.Code).Decode());
}
}

[Test]
public void ShouldNormalizeFullCodeDigitsToExpectedCode() {
foreach (var testData in TestDataList) {
Assert.AreEqual(testData.Code, new OpenLocationCode(testData.CodeDigits).Code,
$"Wrong code normalized from code digits {testData.CodeDigits}");
$"Wrong code encoded for latitude {testData.Latitude} and longitude {testData.Longitude}.");
}
}

[Test]
public void ShouldTrimCodesIntoToExpectedCodeDigits() {
foreach (var testData in TestDataList) {
Assert.AreEqual(testData.CodeDigits, new OpenLocationCode(testData.Code).CodeDigits,
$"Wrong digits trimmed from code {testData.Code}.");
}
}

[Test]
public void ShouldThrowArgumentExceptionForInvalidOrShortCodes() {
foreach (string code in new[] { null, "INVALID", "9QCJ+2VX" }) {
Assert.Throws<ArgumentException>(() => new OpenLocationCode(code),
$"Expected exception was not thrown for code {code}");
foreach (var testData in EncodingTestCases) {
OpenLocationCode olc = new OpenLocationCode(testData.Latitude, testData.Longitude, testData.Length);
int expectedLength = Math.Min(testData.Length, 15);
Assert.AreEqual(expectedLength, olc.CodeDigits.Length,
$"Wrong length of digits trimmed for encoded latitude {testData.Latitude} and longitude {testData.Longitude}.");
Assert.AreEqual(testData.Code.Replace("+", "").Substring(0, expectedLength), olc.CodeDigits,
$"Wrong digits trimmed for encoded latitude {testData.Latitude} and longitude {testData.Longitude}.");
}
}
}

private static void AssertExpectedDecodedArea(TestData testData, CodeArea decodedArea) {
Assert.True(IsNear(testData.DecodedArea.SouthLatitude, decodedArea.SouthLatitude),
$"Wrong decoded low latitude for code {testData.Code}");
Assert.True(IsNear(testData.DecodedArea.NorthLatitude, decodedArea.NorthLatitude),
$"Wrong decoded high latitude for code {testData.Code}");
Assert.True(IsNear(testData.DecodedArea.WestLongitude, decodedArea.WestLongitude),
$"Wrong decoded low longitude for code {testData.Code}");
Assert.True(IsNear(testData.DecodedArea.EastLongitude, decodedArea.EastLongitude),
$"Wrong decoded high longitude for code {testData.Code}");
}

private static bool IsNear(double a, double b) {
return Math.Abs(a - b) < 1e-10;
}


private struct TestData {

internal TestData(string code, string codeDigits, double lat, double lon, double decodedMinLat, double decodedMinLon, double decodedMaxLat, double decodedMaxLon) {
Code = code;
CodeDigits = codeDigits;
EncodedLatitude = lat;
EncodedLongitude = lon;
DecodedArea = new CodeArea(decodedMinLat, decodedMinLon, decodedMaxLat, decodedMaxLon);
}
public class TestData {

internal string Code { get; }
internal string CodeDigits { get; }
internal double EncodedLatitude { get; }
internal double EncodedLongitude { get; }
internal CodeArea DecodedArea { get; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public int Length { get; set; }
public string Code { get; set; }

}
}
1 change: 1 addition & 0 deletions OpenLocationCode.Test/OpenLocationCode.Test.csproj
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CsvHelper" Version="12.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
72 changes: 19 additions & 53 deletions OpenLocationCode.Test/ShorteningTest.cs
Original file line number Diff line number Diff line change
@@ -5,50 +5,23 @@

public static class ShorteningTest {

// Test cases for shortening full codes and recovering short codes.
// test_type is R for recovery only, S for shorten only, or B for both.
// See: https://github.com/google/open-location-code/blob/master/test_data/shortCodeTests.csv
private static readonly List<TestData> TestDataList = new List<TestData> {
new TestData("9C3W9QCJ+2VX", 51.3701125, -1.217765625, "+2VX", "B"),
// Adjust so we can't trim by 8 (+/- .000755)
new TestData("9C3W9QCJ+2VX", 51.3708675, -1.217765625, "CJ+2VX", "B"),
new TestData("9C3W9QCJ+2VX", 51.3693575, -1.217765625, "CJ+2VX", "B"),
new TestData("9C3W9QCJ+2VX", 51.3701125, -1.218520625, "CJ+2VX", "B"),
new TestData("9C3W9QCJ+2VX", 51.3701125, -1.217010625, "CJ+2VX", "B"),
// Adjust so we can't trim by 6 (+/- .0151)
new TestData("9C3W9QCJ+2VX", 51.3852125, -1.217765625, "9QCJ+2VX", "B"),
new TestData("9C3W9QCJ+2VX", 51.3550125, -1.217765625, "9QCJ+2VX", "B"),
new TestData("9C3W9QCJ+2VX", 51.3701125, -1.232865625, "9QCJ+2VX", "B"),
new TestData("9C3W9QCJ+2VX", 51.3701125, -1.202665625, "9QCJ+2VX", "B"),
// Added to detect error in recoverNearest functionality
new TestData("8FJFW222+", 42.899, 9.012, "22+", "B"),
new TestData("796RXG22+", 14.95125, -23.5001, "22+", "B"),
// Reference location is in the 4 digit cell to the south.
new TestData("8FVC2GGG+GG", 46.976, 8.526, "2GGG+GG", "B"),
// Reference location is in the 4 digit cell to the north.
new TestData("8FRCXGGG+GG", 47.026, 8.526, "XGGG+GG", "B"),
// Reference location is in the 4 digit cell to the east.
new TestData("8FR9GXGG+GG", 46.526, 8.026, "GXGG+GG", "B"),
// Reference location is in the 4 digit cell to the west.
new TestData("8FRCG2GG+GG", 46.526, 7.976, "G2GG+GG", "B"),
// Added to detect errors recovering codes near the poles.
// This tests recovery function, but these codes won't shorten.
new TestData("CFX22222+22", 89.6, 0.0, "2222+22", "R"),
new TestData("2CXXXXXX+XX", -81.0, 0.0, "XXXXXX+XX", "R")
};
// Test cases for validating codes and determining code type
// https://github.com/google/open-location-code/blob/master/test_data/validityTests.csv
private static readonly IEnumerable<TestData> ShorteningTestCases = TestDataUtils.ReadTestData<TestData>("shortening.csv");


public class TheShortenMethod {
[Test]
public void ShouldShortenFullCodeToShortCodeFromReferencePoint() {
foreach (var testData in TestDataList) {
if (testData.TestType != "B" && testData.TestType != "S") {
foreach (var testData in ShorteningTestCases) {
if (testData.TestType != 'B' && testData.TestType != 'S') {
continue;
}

OpenLocationCode.ShortCode shortened = OpenLocationCode.Shorten(testData.Code,
testData.ReferenceLatitude, testData.ReferenceLongitude);
testData.Latitude, testData.Longitude);
Assert.AreEqual(testData.ShortCode, shortened.Code,
$"Wrong shortening of code {testData.Code} from reference latitude {testData.ReferenceLatitude} and longitude {testData.ReferenceLongitude}.");
$"Wrong shortening of code {testData.Code} from reference latitude {testData.Latitude} and longitude {testData.Longitude}.");
}
}

@@ -64,14 +37,14 @@ public void ShouldThrowArgumentExceptionForInvalidOrShortOrPaddedCodes() {
public class TheRecoverNearestMethod {
[Test]
public void ShouldRecoverShortCodeToLongCodeFromReferencePoint() {
foreach (var testData in TestDataList) {
if (testData.TestType != "B" && testData.TestType != "R") {
foreach (var testData in ShorteningTestCases) {
if (testData.TestType != 'B' && testData.TestType != 'R') {
continue;
}
OpenLocationCode recovered = OpenLocationCode.ShortCode.RecoverNearest(testData.ShortCode,
testData.ReferenceLatitude, testData.ReferenceLongitude);
testData.Latitude, testData.Longitude);
Assert.AreEqual(testData.Code, recovered.Code,
$"Wrong recovery of short code {testData.ShortCode} from reference latitude {testData.ReferenceLatitude} and longitude {testData.ReferenceLongitude}.");
$"Wrong recovery of short code {testData.ShortCode} from reference latitude {testData.Latitude} and longitude {testData.Longitude}.");
}
}

@@ -96,21 +69,14 @@ public void ShouldThrowArgumentExceptionForInvalidShortCodes() {
}
}

private struct TestData {

internal TestData(string code, double referenceLatitude, double referenceLongitude, string shortCode, string testType) {
Code = code;
ReferenceLatitude = referenceLatitude;
ReferenceLongitude = referenceLongitude;
ShortCode = shortCode;
TestType = testType;
}
public class TestData {

internal string Code { get; }
internal double ReferenceLatitude { get; }
internal double ReferenceLongitude { get; }
internal string ShortCode { get; }
internal string TestType { get; }
public string Code { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public string ShortCode { get; set; }
public char TestType { get; set; }

}

}
29 changes: 29 additions & 0 deletions OpenLocationCode.Test/TestData/decoding.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Test decoding Open Location Codes.
#
# Provides test cases for decoding valid codes.
#
# Format:
# code,length,latLo,lngLo,latHi,lngHi
7FG49Q00+,6,20.35,2.75,20.4,2.8
7FG49QCJ+2V,10,20.37,2.782125,20.370125,2.78225
7FG49QCJ+2VX,11,20.3701,2.78221875,20.370125,2.78225
7FG49QCJ+2VXGJ,13,20.370113,2.782234375,20.370114,2.78223632813
8FVC2222+22,10,47.0,8.0,47.000125,8.000125
4VCPPQGP+Q9,10,-41.273125,174.785875,-41.273,174.786
62G20000+,4,0.0,-180.0,1,-179
22220000+,4,-90,-180,-89,-179
7FG40000+,4,20.0,2.0,21.0,3.0
22222222+22,10,-90.0,-180.0,-89.999875,-179.999875
6VGX0000+,4,0,179,1,180
6FH32222+222,11,1,1,1.000025,1.00003125
# Special cases over 90 latitude and 180 longitude
CFX30000+,4,89,1,90,2
CFX30000+,4,89,1,90,2
62H20000+,4,1,-180,2,-179
62H30000+,4,1,-179,2,-178
CFX3X2X2+X2,10,89.9998750,1,90,1.0001250
# Test non-precise latitude/longitude value
6FH56C22+22,10,1.2000000000000028,3.4000000000000057,1.2001249999999999,3.4001250000000027
# Validate that digits after the first 15 are ignored when decoding
849VGJQF+VX7QR3J,15,37.5396691200,-122.3750698242,37.5396691600,-122.3750697021
849VGJQF+VX7QR3J7QR3J,15,37.5396691200,-122.3750698242,37.5396691600,-122.3750697021
115 changes: 115 additions & 0 deletions OpenLocationCode.Test/TestData/encoding.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Test encoding Open Location Codes.
#
# Provides test cases for encoding latitude and longitude to codes.
#
# Format:
# latitude,longitude,length,expected code (empty if the input should cause an error)
20.375,2.775,6,7FG49Q00+
20.3700625,2.7821875,10,7FG49QCJ+2V
20.3701125,2.782234375,11,7FG49QCJ+2VX
20.3701135,2.78223535156,13,7FG49QCJ+2VXGJ
47.0000625,8.0000625,10,8FVC2222+22
-41.2730625,174.7859375,10,4VCPPQGP+Q9
0.5,-179.5,4,62G20000+
-89.5,-179.5,4,22220000+
20.5,2.5,4,7FG40000+
-89.9999375,-179.9999375,10,22222222+22
0.5,179.5,4,6VGX0000+
1,1,11,6FH32222+222
################################################################################
#
# Special cases over 90 latitude and 180 longitude
#
################################################################################
90,1,4,CFX30000+
92,1,4,CFX30000+
1,180,4,62H20000+
1,181,4,62H30000+
90,1,10,CFX3X2X2+X2
################################################################################
#
# Test non-precise latitude/longitude value
#
################################################################################
1.2,3.4,10,6FH56C22+22
################################################################################
#
# Validate that codes generated with a length exceeding 15 significant digits
# return a 15-digit code
#
################################################################################
37.539669125,-122.375069724,15,849VGJQF+VX7QR3J
37.539669125,-122.375069724,16,849VGJQF+VX7QR3J
37.539669125,-122.375069724,100,849VGJQF+VX7QR3J
################################################################################
#
# Test floating point representation/rounding errors.
#
################################################################################
35.6,3.033,10,8F75J22M+26
-48.71,142.78,8,4R347QRJ+
-70,163.7,8,3V252P22+
-2.804,7.003,13,6F9952W3+C6222
13.9,164.88,12,7V56WV2J+2222
-13.23,172.77,8,5VRJQQCC+
40.6,129.7,8,8QGFJP22+
-52.166,13.694,14,3FVMRMMV+JJ2222
-14,106.9,6,5PR82W00+
70.3,-87.64,13,C62J8926+22222
66.89,-106,10,95RPV2R2+22
2.5,-64.23,11,67JQGQ2C+222
-56.7,-47.2,14,38MJ8R22+222222
-34.45,-93.719,6,46Q8H700+
-35.849,-93.75,12,46P85722+C222
65.748,24.316,12,9GQ6P8X8+6C22
-57.32,130.43,12,3QJGMCJJ+2222
17.6,-44.4,6,789QJJ00+
-27.6,-104.8,6,554QC600+
41.87,-145.59,13,83HPVCC6+22222
-4.542,148.638,13,6R7CFJ5Q+66222
-37.014,-159.936,10,43J2X3P7+CJ
-57.25,125.49,15,3QJ7QF2R+2222222
48.89,-80.52,13,86WXVFRJ+22222
53.66,170.97,14,9V5GMX6C+222222
0.49,-76.97,15,67G5F2RJ+2222222
40.44,-36.7,12,89G5C8R2+2222
58.73,69.95,8,9JCFPXJ2+
16.179,150.075,12,7R8G53HG+J222
-55.574,-70.061,12,37PFCWGQ+CJ22
76.1,-82.5,15,C68V4G22+2222222
58.66,149.17,10,9RCFM56C+22
-67.2,48.6,6,3H4CRJ00+
-5.6,-54.5,14,6867CG22+222222
-34,145.5,14,4RR72G22+222222
-34.2,66.4,12,4JQ8RC22+2222
17.8,-108.5,6,759HRG00+
10.734,-168.294,10,722HPPM4+JC
-28.732,54.32,8,5H3P789C+
64.1,107.9,12,9PP94W22+2222
79.7525,6.9623,8,CFF8QX36+
-63.6449,-25.1475,8,398P9V43+
35.019,148.827,11,8R7C2R9G+JR2
71.132,-98.584,15,C6334CJ8+RC22222
53.38,-51.34,12,985C9MJ6+2222
-1.2,170.2,12,6VCGR622+2222
50.2,-162.8,11,922V6622+222
-25.798,-59.812,10,5862652Q+R6
81.654,-162.422,14,C2HVMH3H+J62222
-75.7,-35.4,8,29P68J22+
-80.01,58.57,15,2HFWXHRC+2222222
67.2,115.1,11,9PVQ6422+222
-78.137,-42.995,12,28HVV274+6222
-56.3,114.5,11,3PMPPG22+222
10.767,-62.787,13,772VQ687+R6222
-19.212,107.423,10,5PG9QCQF+66
21.192,-45.145,15,78HP5VR4+R222222
16.701,148.648,14,7R8CPJ2X+C62222
52.25,-77.45,15,97447H22+2222222
-68.54504,-62.81725,11,373VF53M+X4J
76.7,-86.172,12,C68MPR2H+2622
-6.2,96.6,13,6M5RRJ22+22222
59.32,-157.21,12,93F48QCR+2222
29.7,39.6,12,7GXXPJ22+2222
-18.32,96.397,10,5MHRM9JW+2R
-30.3,76.5,11,4JXRPG22+222
50.342,-112.534,15,95298FR8+RC22222
35 changes: 35 additions & 0 deletions OpenLocationCode.Test/TestData/shortening.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Test shortening and extending codes.
#
# Format:
# full code,lat,lng,shortcode,test_type
# test_type is R for recovery only, S for shorten only, or B for both.
9C3W9QCJ+2VX,51.3701125,-1.217765625,+2VX,B
# Adjust so we can't trim by 8 (+/- .000755)
9C3W9QCJ+2VX,51.3708675,-1.217765625,CJ+2VX,B
9C3W9QCJ+2VX,51.3693575,-1.217765625,CJ+2VX,B
9C3W9QCJ+2VX,51.3701125,-1.218520625,CJ+2VX,B
9C3W9QCJ+2VX,51.3701125,-1.217010625,CJ+2VX,B
# Adjust so we can't trim by 6 (+/- .0151)
9C3W9QCJ+2VX,51.3852125,-1.217765625,9QCJ+2VX,B
9C3W9QCJ+2VX,51.3550125,-1.217765625,9QCJ+2VX,B
9C3W9QCJ+2VX,51.3701125,-1.232865625,9QCJ+2VX,B
9C3W9QCJ+2VX,51.3701125,-1.202665625,9QCJ+2VX,B
# Added to detect error in recoverNearest functionality
8FJFW222+,42.899,9.012,22+,B
796RXG22+,14.95125,-23.5001,22+,B
# Reference location is in the 4 digit cell to the south.
8FVC2GGG+GG,46.976,8.526,2GGG+GG,B
# Reference location is in the 4 digit cell to the north.
8FRCXGGG+GG,47.026,8.526,XGGG+GG,B
# Reference location is in the 4 digit cell to the east.
8FR9GXGG+GG,46.526,8.026,GXGG+GG,B
# Reference location is in the 4 digit cell to the west.
8FRCG2GG+GG,46.526,7.976,G2GG+GG,B
# Added to detect errors recovering codes near the poles.
# This tests recovery function, but these codes won't shorten.
CFX22222+22,89.6,0.0,2222+22,R
2CXXXXXX+XX,-81.0,0.0,XXXXXX+XX,R
# Recovered full codes should be the full code
8FRCG2GG+GG,46.526,7.976,8FRCG2GG+GG,R
# Recovered full codes should be the uppercased full code
8FRCG2GG+GG,46.526,7.976,8frCG2GG+gG,R
33 changes: 33 additions & 0 deletions OpenLocationCode.Test/TestData/validity.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Test data for validity tests.
# Format of each line is:
# code,isValid,isShort,isFull,isPadded
# Valid full codes:
8FWC2345+G6,true,false,true,false
8FWC2345+G6G,true,false,true,false
8fwc2345+,true,false,true,false
8FWCX400+,true,false,true,true
8FWC0000+,true,false,true,true
8F000000+,true,false,true,true
# Valid short codes:
WC2345+G6g,true,true,false,false
2345+G6,true,true,false,false
45+G6,true,true,false,false
+G6,true,true,false,false
# Invalid codes
G+,false,false,false,false
+,false,false,false,false
8FWC2345+G,false,false,false,false
8FWC2_45+G6,false,false,false,false
8FWC2η45+G6,false,false,false,false
8FWC2345+G6+,false,false,false,false
8FWC2345G6+,false,false,false,false
8FWC2300+G6,false,false,false,false
WC2300+G6g,false,false,false,false
WC2345+G,false,false,false,false
WC2300+,false,false,false,false
# Validate that codes at and exceeding 15 digits are still valid when all their
# digits are valid, and invalid when not.
849VGJQF+VX7QR3J,true,false,true,false
849VGJQF+VX7QR3U,false,false,false,false
849VGJQF+VX7QR3JW,true,false,true,false
849VGJQF+VX7QR3JU,false,false,false,false
17 changes: 17 additions & 0 deletions OpenLocationCode.Test/TestDataUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Immutable;
using System.IO;
using CsvHelper;
using CsvHelper.Configuration;


public static class TestDataUtils {

public static ImmutableList<T> ReadTestData<T>(string fileName) {
using (var reader = new StreamReader($"..\\..\\..\\TestData\\{fileName}")) {
using (var csv = new CsvReader(reader, new Configuration { HasHeaderRecord = false, AllowComments = true })) {
return csv.GetRecords<T>().ToImmutableList();
}
}
}

}
69 changes: 14 additions & 55 deletions OpenLocationCode.Test/ValidityTest.cs
Original file line number Diff line number Diff line change
@@ -4,41 +4,15 @@

public static class ValidityTest {

// Test cases for validating codes and determining code type.
// See: https://github.com/google/open-location-code/blob/master/test_data/validityTests.csv
private static readonly List<TestData> TestDataList = new List<TestData> {
// Valid full codes:
FullCode("8FWC2345+G6"),
FullCode("8FWC2345+G6G"),
FullCode("8fwc2345+"),
FullCode("8FWCX400+", padded: true),
FullCode("8FWC0000+", padded: true),
FullCode("8F000000+", padded: true),
// Valid short codes:
ShortCode("WC2345+G6g"),
ShortCode("2345+G6"),
ShortCode("45+G6"),
ShortCode("+G6"),
// Invalid codes:
InvalidCode("G+"),
InvalidCode("+"),
InvalidCode("8FWC2345+G"),
InvalidCode("8FWC2_45+G6"),
InvalidCode("8FWC2η45+G6"),
InvalidCode("8FWC2345+G6+"),
InvalidCode("8FWC2345G6+"),
InvalidCode("8FWC2300+G6"),
InvalidCode("8FWC2300+00"),
InvalidCode("WC2300+G6g"),
InvalidCode("WC2345+G"),
InvalidCode("WC2300+")
};
// Test cases for validating codes and determining code type
// https://github.com/google/open-location-code/blob/master/test_data/validityTests.csv
private static readonly IEnumerable<TestData> ValidityTestCases = TestDataUtils.ReadTestData<TestData>("validity.csv");


public class TheIsValidCodeMethod {
public class TheIsValidMethod {
[Test]
public void ShouldDetermineValidityOfACode() {
foreach (TestData testData in TestDataList) {
foreach (TestData testData in ValidityTestCases) {
Assert.AreEqual(testData.IsValid, OpenLocationCode.IsValid(testData.Code),
$"Validity of code {testData.Code} is wrong.");
}
@@ -62,7 +36,7 @@ public void ShouldValidateCodesExceedingMaximumLength() {
public class TheIsShortMethod {
[Test]
public void ShouldDetermineShortnessOfACode() {
foreach (TestData testData in TestDataList) {
foreach (TestData testData in ValidityTestCases) {
Assert.AreEqual(testData.IsShort, OpenLocationCode.IsShort(testData.Code),
$"Shortness of code {testData.Code} is wrong.");
}
@@ -72,7 +46,7 @@ public void ShouldDetermineShortnessOfACode() {
public class TheIsFullMethod {
[Test]
public void ShouldDetermineFullnessOfACode() {
foreach (TestData testData in TestDataList) {
foreach (TestData testData in ValidityTestCases) {
Assert.AreEqual(testData.IsFull, OpenLocationCode.IsFull(testData.Code),
$"Fullness of code {testData.Code} is wrong.");
}
@@ -83,35 +57,20 @@ public void ShouldDetermineFullnessOfACode() {
public class TheIsPaddedMethod {
[Test]
public void ShouldDeterminePaddingOfACode() {
foreach (TestData testData in TestDataList) {
foreach (TestData testData in ValidityTestCases) {
Assert.AreEqual(testData.IsPadded, OpenLocationCode.IsPadded(testData.Code),
$"Padding for code {testData.Code} is wrong.");
}
}
}

public class TestData {

private static TestData FullCode(string code, bool padded = false) => new TestData(code, true, false, true, padded);

private static TestData ShortCode(string code) => new TestData(code, true, true, false, false);

private static TestData InvalidCode(string code) => new TestData(code, false, false, false, false);

private struct TestData {

internal TestData(string code, bool isValid, bool isShort, bool isFull, bool isPadded) {
Code = code;
IsValid = isValid;
IsShort = isShort;
IsFull = isFull;
IsPadded = isPadded; // Nonstandard
}

internal string Code { get; }
internal bool IsValid { get; }
internal bool IsShort { get; }
internal bool IsFull { get; }
internal bool IsPadded { get; }
public string Code { get; set; }
public bool IsValid { get; set; }
public bool IsShort { get; set; }
public bool IsFull { get; set; }
public bool IsPadded { get; set; }

}

78 changes: 8 additions & 70 deletions OpenLocationCode/CodeArea.cs
Original file line number Diff line number Diff line change
@@ -2,86 +2,24 @@

namespace Google.OpenLocationCode {
/// <summary>
/// Coordinates of a decoded Open Location Code area.
/// The coordinates include the latitude and longitude of the lower left (south west) and upper right (north east) corners
/// and the center of the bounding box of the code area.
/// A square <see cref="GeoArea"/> for the coordinates of a decoded Open Location Code area.
/// The <see cref="CodeLength"/> of the decoded Open Location Code is also included.
/// </summary>
public class CodeArea {
public class CodeArea : GeoArea {

internal CodeArea(double southLatitude, double westLongitude, double northLatitude, double eastLongitude) {
internal CodeArea(double southLatitude, double westLongitude, double northLatitude, double eastLongitude, int codeLength) :
base(southLatitude, westLongitude, northLatitude, eastLongitude) {
if (southLatitude >= northLatitude || westLongitude >= eastLongitude) {
throw new ArgumentException("min must be less than max");
}

Min = new GeoPoint(southLatitude, westLongitude);
Max = new GeoPoint(northLatitude, eastLongitude);
CodeLength = codeLength;
}

/// <summary>
/// The min (south west) point coordinates of the area bounds.
/// The length of the decoded Open Location Code.
/// </summary>
public GeoPoint Min { get; }

/// <summary>
/// The max (north east) point coordinates of the area bounds.
/// </summary>
public GeoPoint Max { get; }

/// <summary>
/// The center point of the area which is equidistant between <see cref="Min"/> and <see cref="Max"/>.
/// </summary>
public GeoPoint Center => new GeoPoint(CenterLatitude, CenterLongitude);


/// <summary>
/// The width of the area in longitude degrees.
/// </summary>
public double LongitudeWidth => (double) ((decimal) Max.Longitude - (decimal) Min.Longitude);

/// <summary>
/// The height of the area in latitude degrees.
/// </summary>
public double LatitudeHeight => (double) ((decimal) Max.Latitude - (decimal) Min.Latitude);


/// <summary>The south (min) latitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Min"/>.<see cref="GeoPoint.Latitude">Latitude</see></remarks>
public double SouthLatitude => Min.Latitude;

/// <summary>The west (min) longitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Min"/>.<see cref="GeoPoint.Longitude">Longitude</see></remarks>
public double WestLongitude => Min.Longitude;

/// <summary>The north (max) latitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Max"/>.<see cref="GeoPoint.Latitude">Latitude</see></remarks>
public double NorthLatitude => Max.Latitude;

/// <summary>The east (max) longitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Max"/>.<see cref="GeoPoint.Longitude">Longitude</see></remarks>
public double EastLongitude => Max.Longitude;

/// <summary>The center latitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Center"/>.<see cref="GeoPoint.Latitude">Latitude</see></remarks>
public double CenterLatitude => (Min.Latitude + Max.Latitude) / 2;

/// <summary>The center longitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Center"/>.<see cref="GeoPoint.Longitude">Longitude</see></remarks>
public double CenterLongitude => (Min.Longitude + Max.Longitude) / 2;


/// <returns><c>true</c> if this code area contains the provided point, <c>false</c> otherwise.</returns>
/// <param name="point">The point coordinates to check.</param>
public bool Contains(GeoPoint point) {
return Contains(point.Latitude, point.Longitude);
}

/// <returns><c>true</c> if this code area contains the provided point, <c>false</c> otherwise.</returns>
/// <param name="latitude">The latitude coordinate of the point to check.</param>
/// <param name="longitude">The longitude coordinate of the point to check.</param>
public bool Contains(double latitude, double longitude) {
return Min.Latitude <= latitude && latitude < Max.Latitude
&& Min.Longitude <= longitude && longitude < Max.Longitude;
}
public int CodeLength { get; }

}
}
105 changes: 105 additions & 0 deletions OpenLocationCode/GeoArea.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;

namespace Google.OpenLocationCode {
/// <summary>
/// A rectangular area on the geographic coordinate system specified by the minimum and maximum <see cref="GeoPoint"/> coordinates.
/// The coordinates include the latitude and longitude of the lower left (south west) and upper right (north east) corners.
/// <para>
/// Additional properties exist to calculate the <see cref="Center"/> of the bounding box,
/// and the <see cref="LatitudeHeight"/> or <see cref="LongitudeWidth"/> area dimensions in degrees.
/// </para>
/// </summary>
public class GeoArea {

/// <summary>
/// Create a new rectangular GeoArea of the provided min and max geo points.
/// </summary>
/// <param name="min">The minimum GeoPoint</param>
/// <param name="max">The maximum GeoPoint</param>
/// <exception cref="ArgumentException">If min is greater than or equal to max.</exception>
public GeoArea(GeoPoint min, GeoPoint max) {
if (min.Latitude >= max.Latitude || min.Longitude >= max.Longitude) {
throw new ArgumentException("min must be less than max");
}
Min = min;
Max = max;
}

/// <summary>
/// Create a new rectangular GeoArea of the provided min and max geo coordinates.
/// </summary>
/// <param name="southLatitude">The minimum south latitude</param>
/// <param name="westLongitude">The minimum west longitude</param>
/// <param name="northLatitude">The maximum north latitude</param>
/// <param name="eastLongitude">The maximum east longitude</param>
public GeoArea(double southLatitude, double westLongitude, double northLatitude, double eastLongitude) :
this(new GeoPoint(southLatitude, westLongitude), new GeoPoint(northLatitude, eastLongitude)) { }

/// <summary>
/// The min (south west) point coordinates of the area bounds.
/// </summary>
public GeoPoint Min { get; }

/// <summary>
/// The max (north east) point coordinates of the area bounds.
/// </summary>
public GeoPoint Max { get; }

/// <summary>
/// The center point of the area which is equidistant between <see cref="Min"/> and <see cref="Max"/>.
/// </summary>
public GeoPoint Center => new GeoPoint(CenterLatitude, CenterLongitude);


/// <summary>
/// The width of the area in longitude degrees.
/// </summary>
public double LongitudeWidth => (double)((decimal)Max.Longitude - (decimal)Min.Longitude);

/// <summary>
/// The height of the area in latitude degrees.
/// </summary>
public double LatitudeHeight => (double)((decimal)Max.Latitude - (decimal)Min.Latitude);


/// <summary>The south (min) latitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Min"/>.<see cref="GeoPoint.Latitude">Latitude</see></remarks>
public double SouthLatitude => Min.Latitude;

/// <summary>The west (min) longitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Min"/>.<see cref="GeoPoint.Longitude">Longitude</see></remarks>
public double WestLongitude => Min.Longitude;

/// <summary>The north (max) latitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Max"/>.<see cref="GeoPoint.Latitude">Latitude</see></remarks>
public double NorthLatitude => Max.Latitude;

/// <summary>The east (max) longitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Max"/>.<see cref="GeoPoint.Longitude">Longitude</see></remarks>
public double EastLongitude => Max.Longitude;

/// <summary>The center latitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Center"/>.<see cref="GeoPoint.Latitude">Latitude</see></remarks>
public double CenterLatitude => (Min.Latitude + Max.Latitude) / 2;

/// <summary>The center longitude coordinate in decimal degrees.</summary>
/// <remarks>Alias to <see cref="Center"/>.<see cref="GeoPoint.Longitude">Longitude</see></remarks>
public double CenterLongitude => (Min.Longitude + Max.Longitude) / 2;


/// <returns><c>true</c> if this geo area contains the provided point, <c>false</c> otherwise.</returns>
/// <param name="point">The point coordinates to check.</param>
public bool Contains(GeoPoint point) {
return Contains(point.Latitude, point.Longitude);
}

/// <returns><c>true</c> if this geo area contains the provided point, <c>false</c> otherwise.</returns>
/// <param name="latitude">The latitude coordinate of the point to check.</param>
/// <param name="longitude">The longitude coordinate of the point to check.</param>
public bool Contains(double latitude, double longitude) {
return Min.Latitude <= latitude && latitude < Max.Latitude
&& Min.Longitude <= longitude && longitude < Max.Longitude;
}

}
}
236 changes: 130 additions & 106 deletions OpenLocationCode/OpenLocationCode.cs

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions OpenLocationCode/OpenLocationCode.csproj
Original file line number Diff line number Diff line change
@@ -2,10 +2,22 @@

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>2.1.0</Version>
<Authors>Jon McPherson</Authors>
<Product>OpenLocationCode</Product>
<Owner>JonMcPherson</Owner>
<PackageProjectUrl>https://plus.codes/</PackageProjectUrl>
<Description>The C# (.NET Standard) implementation of the Google Open Location Code API.</Description>
<PackageTags>OLC google open location code plus codes latitude longitude encode decode</PackageTags>
<Company />
<PackageReleaseNotes>See the pull request on Github: https://github.com/JonMcPherson/open-location-code/pull/9</PackageReleaseNotes>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\Debug\netstandard2.0\OpenLocationCode.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\Release\netstandard2.0\OpenLocationCode.xml</DocumentationFile>
</PropertyGroup>

</Project>
22 changes: 10 additions & 12 deletions OpenLocationCode/OpenLocationCode.nuspec
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<?xml version="1.0"?>
<package >
<metadata>
<id>OpenLocationCode</id>
<version>2.0.0</version>
<title>OpenLocationCode</title>
<authors>Jon McPherson</authors>
<owners>JonMcPherson</owners>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>$author$</authors>
<owners>$owner$</owners>
<licenseUrl>https://github.com/google/open-location-code/blob/master/LICENSE</licenseUrl>
<projectUrl>https://plus.codes/</projectUrl>
<repository type="git" url="https://github.com/JonMcPherson/open-location-code" />
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>The C# (.NET Standard) implementation of the Google Open Location Code API.</description>
<tags>OLC google open location code plus codes latitude longitude encode decode</tags>
<description>$description$</description>
<releaseNotes>$packageReleaseNotes$</releaseNotes>
<tags>$packageTags$</tags>
<dependencies>
<group targetFramework=".NETStandard2.0" />
</dependencies>
</metadata>
<files>
<file src="bin\$configuration$\netstandard2.0\OpenLocationCode.dll" target="lib\netstandard2.0\OpenLocationCode.dll" />
</files>
</package>

0 comments on commit a2bcf65

Please sign in to comment.