Skip to content

Commit d47385e

Browse files
committed
Update C implementation to expose integer conversion and encoding functions.
1 parent 241d1e4 commit d47385e

2 files changed

Lines changed: 72 additions & 38 deletions

File tree

c/src/olc.c

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,13 @@ int OLC_IsFull(const char* code, size_t size) {
8282
return is_full(&info);
8383
}
8484

85-
int OLC_Encode(const OLC_LatLon* location, size_t length, char* code,
86-
int maxlen) {
85+
void OLC_LocationToIntegers(const OLC_LatLon* degrees,
86+
OLC_LatLonIntegers* integers) {
8787
// Multiply degrees by precision. Use lround to explicitly round rather than
8888
// truncate, which causes issues when using values like 0.1 that do not have
8989
// precise floating point representations.
90-
long long int lat = lround(location->lat * kGridLatPrecisionInverse);
91-
long long int lng = lround(location->lon * kGridLonPrecisionInverse);
92-
// Limit the maximum number of digits in the code.
93-
if (length > kMaximumDigitCount) {
94-
length = kMaximumDigitCount;
95-
}
96-
if (length < kMinimumDigitCount) {
97-
length = kMinimumDigitCount;
98-
}
99-
if (length < kPairCodeLength && length % 2 == 1) {
100-
length = length + 1;
101-
}
90+
long long int lat = lround(degrees->lat * kGridLatPrecisionInverse);
91+
long long int lon = lround(degrees->lon * kGridLonPrecisionInverse);
10292
// Convert latitude to positive range (0..2*degrees*precision) and clip.
10393
lat += OLC_kLatMaxDegrees * kGridLatPrecisionInverse;
10494
if (lat < 0) {
@@ -108,16 +98,35 @@ int OLC_Encode(const OLC_LatLon* location, size_t length, char* code,
10898
lat = 2 * OLC_kLatMaxDegrees * kGridLatPrecisionInverse - 1;
10999
}
110100
// Convert longitude to the positive range and normalise.
111-
lng += OLC_kLonMaxDegrees * kGridLonPrecisionInverse;
112-
if (lng < 0) {
101+
lon += OLC_kLonMaxDegrees * kGridLonPrecisionInverse;
102+
if (lon < 0) {
113103
// If after adding 180 it is still less than zero, do integer division
114104
// on a full longitude (360) and add the remainder.
115-
lng = lng % (2 * OLC_kLonMaxDegrees * kGridLonPrecisionInverse) +
105+
lon = lon % (2 * OLC_kLonMaxDegrees * kGridLonPrecisionInverse) +
116106
(2 * OLC_kLonMaxDegrees * kGridLonPrecisionInverse);
117-
} else if (lng >= 2 * OLC_kLonMaxDegrees * kGridLonPrecisionInverse) {
107+
} else if (lon >= 2 * OLC_kLonMaxDegrees * kGridLonPrecisionInverse) {
118108
// If it's greater than 360, just get the integer division remainder.
119-
lng = lng % (2 * OLC_kLonMaxDegrees * kGridLonPrecisionInverse);
109+
lon = lon % (2 * OLC_kLonMaxDegrees * kGridLonPrecisionInverse);
120110
}
111+
integers->lat = lat;
112+
integers->lon = lon;
113+
}
114+
115+
int OLC_EncodeIntegers(const OLC_LatLonIntegers* location, size_t length,
116+
char* code, int maxlen) {
117+
// Limit the maximum number of digits in the code.
118+
if (length > kMaximumDigitCount) {
119+
length = kMaximumDigitCount;
120+
}
121+
if (length < kMinimumDigitCount) {
122+
length = kMinimumDigitCount;
123+
}
124+
if (length < kPairCodeLength && length % 2 == 1) {
125+
length = length + 1;
126+
}
127+
128+
long long int lat = location->lat;
129+
long long int lon = location->lon;
121130

122131
// Reserve characters for the code digits, the separator and the null
123132
// terminator.
@@ -129,30 +138,30 @@ int OLC_Encode(const OLC_LatLon* location, size_t length, char* code,
129138
if (length > kPairCodeLength) {
130139
for (size_t i = kMaximumDigitCount - kPairCodeLength; i >= 1; i--) {
131140
int lat_digit = lat % kGridRows;
132-
int lng_digit = lng % kGridCols;
141+
int lng_digit = lon % kGridCols;
133142
fullcode[kSeparatorPosition + 2 + i] =
134143
kAlphabet[lat_digit * kGridCols + lng_digit];
135144
lat /= kGridRows;
136-
lng /= kGridCols;
145+
lon /= kGridCols;
137146
}
138147
} else {
139148
lat /= pow(kGridRows, kGridCodeLength);
140-
lng /= pow(kGridCols, kGridCodeLength);
149+
lon /= pow(kGridCols, kGridCodeLength);
141150
}
142151

143152
// Add the pair after the separator.
144153
fullcode[kSeparatorPosition + 1] = kAlphabet[lat % kEncodingBase];
145-
fullcode[kSeparatorPosition + 2] = kAlphabet[lng % kEncodingBase];
154+
fullcode[kSeparatorPosition + 2] = kAlphabet[lon % kEncodingBase];
146155
lat /= kEncodingBase;
147-
lng /= kEncodingBase;
156+
lon /= kEncodingBase;
148157

149158
// Compute the pair section before the separator in reverse order.
150159
// Even indices contain latitude and odd contain longitude.
151160
for (int i = (kPairCodeLength / 2 + 1); i >= 0; i -= 2) {
152161
fullcode[i] = kAlphabet[lat % kEncodingBase];
153-
fullcode[i + 1] = kAlphabet[lng % kEncodingBase];
162+
fullcode[i + 1] = kAlphabet[lon % kEncodingBase];
154163
lat /= kEncodingBase;
155-
lng /= kEncodingBase;
164+
lon /= kEncodingBase;
156165
}
157166
// Replace digits with padding if necessary.
158167
if (length < kSeparatorPosition) {
@@ -170,6 +179,13 @@ int OLC_Encode(const OLC_LatLon* location, size_t length, char* code,
170179
return length;
171180
}
172181

182+
int OLC_Encode(const OLC_LatLon* location, size_t length, char* code,
183+
int maxlen) {
184+
OLC_LatLonIntegers integers;
185+
OLC_LocationToIntegers(location, &integers);
186+
return OLC_EncodeIntegers(&integers, length, code, maxlen);
187+
}
188+
173189
int OLC_EncodeDefault(const OLC_LatLon* location, char* code, int maxlen) {
174190
return OLC_Encode(location, kPairCodeLength, code, maxlen);
175191
}

c/src/olc.h

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,53 +22,71 @@
2222
#define OLC_MAKE_VERSION_STR(major, minor, patch) \
2323
OLC_MAKE_VERSION_STR_IMPL(major, minor, patch)
2424

25-
// Current version, as a number and a string
25+
// Current version, as a number and a string.
2626
#define OLC_VERSION_NUM \
2727
OLC_MAKE_VERSION_NUM(OLC_VERSION_MAJOR, OLC_VERSION_MINOR, OLC_VERSION_PATCH)
2828
#define OLC_VERSION_STR \
2929
OLC_MAKE_VERSION_STR(OLC_VERSION_MAJOR, OLC_VERSION_MINOR, OLC_VERSION_PATCH)
3030

31-
// A pair of doubles representing latitude / longitude
31+
// A pair of doubles representing latitude and longitude.
3232
typedef struct OLC_LatLon {
3333
double lat;
3434
double lon;
3535
} OLC_LatLon;
3636

37-
// An area defined by two corners (lo and hi) and a code length
37+
// A pair of clipped, normalised positive range integers representing latitude
38+
// and longitude. This is used internally in the encoding methods to avoid
39+
// floating-point precision issues.
40+
typedef struct OLC_LatLonIntegers {
41+
long long int lat;
42+
long long int lon;
43+
} OLC_LatLonIntegers;
44+
45+
// Convert a location in degrees into the integer values necessary to reliably
46+
// encode it.
47+
void OLC_LocationToIntegers(const OLC_LatLon* degrees,
48+
OLC_LatLonIntegers* integers);
49+
50+
// An area defined by two corners (lo and hi) and a code length.
3851
typedef struct OLC_CodeArea {
3952
OLC_LatLon lo;
4053
OLC_LatLon hi;
4154
size_t len;
4255
} OLC_CodeArea;
4356

44-
// Get the center coordinates for an area
57+
// Get the center coordinates for an area.
4558
void OLC_GetCenter(const OLC_CodeArea* area, OLC_LatLon* center);
4659

47-
// Get the effective length for a code
60+
// Get the effective length for a code.
4861
size_t OLC_CodeLength(const char* code, size_t size);
4962

5063
// Check for the three obviously-named conditions
5164
int OLC_IsValid(const char* code, size_t size);
5265
int OLC_IsShort(const char* code, size_t size);
5366
int OLC_IsFull(const char* code, size_t size);
5467

55-
// Encode location with given code length (indicates precision) into an OLC
56-
// Return the string length of the code
68+
// Encode location with given code length (indicates precision) into an OLC.
69+
// Return the string length of the code.
5770
int OLC_Encode(const OLC_LatLon* location, size_t code_length, char* code,
5871
int maxlen);
5972

60-
// Encode location with default code length into an OLC
61-
// Return the string length of the code
73+
// Encode using integer values. This is only exposed for testing and should
74+
// not be called from client code.
75+
int OLC_EncodeIntegers(const OLC_LatLonIntegers* location, size_t code_length,
76+
char* code, int maxlen);
77+
78+
// Encode location with default code length into an OLC.
79+
// Return the string length of the code.
6280
int OLC_EncodeDefault(const OLC_LatLon* location, char* code, int maxlen);
6381

64-
// Decode OLC into the original location
82+
// Decode OLC into the original location.
6583
int OLC_Decode(const char* code, size_t size, OLC_CodeArea* decoded);
6684

67-
// Compute a (shorter) OLC for a given code and a reference location
85+
// Compute a (shorter) OLC for a given code and a reference location.
6886
int OLC_Shorten(const char* code, size_t size, const OLC_LatLon* reference,
6987
char* buf, int maxlen);
7088

71-
// Given shorter OLC and reference location, compute original (full length) OLC
89+
// Given shorter OLC and reference location, compute original (full length) OLC.
7290
int OLC_RecoverNearest(const char* short_code, size_t size,
7391
const OLC_LatLon* reference, char* code, int maxlen);
7492

0 commit comments

Comments
 (0)