@@ -91,10 +91,10 @@ Private Const LATITUDE_MAX_ As Double = 90
91
91
Private Const LONGITUDE_MAX_ As Double = 180
92
92
93
93
' Minimum number of digits in a code.
94
- Private Const MIN_DIGIT_COUNT_ = 2 ;
94
+ Private Const MIN_DIGIT_COUNT_ As Integer = 2
95
95
96
96
' Maximum number of digits in a code.
97
- Private Const MAX_DIGIT_COUNT_ = 15 ;
97
+ Private Const MAX_DIGIT_COUNT_ As Integer = 15
98
98
99
99
' Maximum code length using lat/lng pair encoding. The area of such a
100
100
' code is approximately 13x13 meters (at the equator), and should be suitable
@@ -107,9 +107,18 @@ Private Const GRID_COLUMNS_ As Integer = 4
107
107
' Number of rows in the grid refinement method.
108
108
Private Const GRID_ROWS_ As Integer = 5
109
109
110
+ ' Number of grid digits.
111
+ Private Const GRID_CODE_LENGTH_ As Integer = MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_
112
+
110
113
' Size of the initial grid in degrees.
111
114
Private Const GRID_SIZE_DEGREES_ As Double = 1 / 8000
112
115
116
+ ' Degree resolution for latitude.
117
+ Private Const FINAL_LAT_PRECISION_ As Long = 8000 * (GRID_ROWS_ ^ GRID_CODE_LENGTH_)
118
+
119
+ ' Degree resolution for longitude.
120
+ Private Const FINAL_LNG_PRECISION_ As Long = 8000 * (GRID_COLUMNS_ ^ GRID_CODE_LENGTH_)
121
+
113
122
' Minimum length of a code that can be shortened.
114
123
Private Const MIN_TRIMMABLE_CODE_LEN_ As Integer = 6
115
124
@@ -227,23 +236,80 @@ Public Function OLCEncode(ByVal latitude As Double, ByVal longitude As Double, O
227
236
If codeLength < PAIR_CODE_LENGTH_ And codeLength \ 2 = 1 Then
228
237
Err.raise vbObjectError + 513 , "OLCEncodeWithLength" , "Invalid code length"
229
238
End If
230
- Dim lat, lng As Double
231
- Dim latCode, lngCode, gridCode As String
232
- Dim code As String
233
- ' Ensure that the latitude and longitude are valid.
234
- lat = clipLatitude(latitude)
235
- lng = normalizeLongitude(longitude)
236
- ' Latitude 90 needs to be adjusted to be just under, so the returned code can also be decoded.
237
- If lat = 90 Then
238
- lat = lat - computeLatitudePrecision(codeLength)
239
+ ' We use Doubles for the latitude and longitude, even though we will use them as integers.
240
+ ' The reason is that we want to use this code in Excel and LibreOffice, but the LibreOffice
241
+ ' Long type is only 32 bits, –2147483648 and 2147483647, which is too small.
242
+ Dim lat As Double , lng As Double
243
+ ' i is used in loops.
244
+ Dim i As Integer
245
+
246
+ ' Convert latitude into a positive integer clipped into the range 0-(just
247
+ ' under 180*2.5e7). Latitude 90 needs to be adjusted to be just less, so the
248
+ ' returned code can also be decoded.
249
+ lat = Round(latitude * FINAL_LAT_PRECISION_)
250
+ lat = lat + LATITUDE_MAX_ * FINAL_LAT_PRECISION_
251
+ If lat < 0 Then
252
+ lat = 0
253
+ ElseIf lat >= 2 * LATITUDE_MAX_ * FINAL_LAT_PRECISION_ Then
254
+ lat = 2 * LATITUDE_MAX_ * FINAL_LAT_PRECISION_ - 1
239
255
End If
240
- latCode = encodeCoordinate(lat + LATITUDE_MAX_, doubleMin(codeLength, PAIR_CODE_LENGTH_) / 2 )
241
- lngCode = encodeCoordinate(lng + LONGITUDE_MAX_, doubleMin(codeLength, PAIR_CODE_LENGTH_) / 2 )
256
+ ' Convert longitude into a positive integer and normalise it into the range 0-360*8.192e6.
257
+ lng = Round(longitude * FINAL_LNG_PRECISION_)
258
+ lng = lng + LONGITUDE_MAX_ * FINAL_LNG_PRECISION_
259
+ If lng < 0 Then
260
+ lng = doubleMod(lng, (2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_)) + 2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_
261
+ ElseIf lng >= 2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_ Then
262
+ lng = doubleMod(lng, (2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_))
263
+ EndIf
264
+
265
+ ' Build up the code in an array.
266
+ Dim code(MAX_DIGIT_COUNT_) As String
267
+ code(SEPARATOR_POSITION_) = SEPARATOR_
268
+
269
+ ' Compute the grid part of the code if necessary.
270
+ Dim latDigit As Integer
271
+ Dim lngDigit As Integer
242
272
If codeLength > PAIR_CODE_LENGTH_ Then
243
- gridCode = encodeGrid(lat, lng, codeLength - PAIR_CODE_LENGTH_)
273
+ For i = MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_ To 1 Step -1
274
+ latDigit = CInt(doubleMod(lat, GRID_ROWS_))
275
+ lngDigit = CInt(doubleMod(lng, GRID_COLUMNS_))
276
+ code(SEPARATOR_POSITION_ + 2 + i) = Mid(CODE_ALPHABET_, 1 + latDigit * GRID_COLUMNS_ + lngDigit, 1 )
277
+ lat = Int(lat / GRID_ROWS_)
278
+ lng = Int(lng / GRID_COLUMNS_)
279
+ Next
280
+ Else
281
+ lat = Int(lat / (GRID_ROWS_ ^ GRID_CODE_LENGTH_))
282
+ lng = Int(lng / (GRID_COLUMNS_ ^ GRID_CODE_LENGTH_))
244
283
End If
245
- code = mergeCode(latCode, lngCode, gridCode)
246
- OLCEncode = code
284
+
285
+ ' Add the pair after the separator.
286
+ code(SEPARATOR_POSITION_ + 1 ) = Mid(CODE_ALPHABET_, 1 + doubleMod(lat, ENCODING_BASE_), 1 )
287
+ code(SEPARATOR_POSITION_ + 2 ) = Mid(CODE_ALPHABET_, 1 + doubleMod(lng, ENCODING_BASE_), 1 )
288
+ lat = Int(lat / ENCODING_BASE_)
289
+ lng = Int(lng / ENCODING_BASE_)
290
+
291
+ ' Compute the pair section of the code.
292
+ For i = Int(PAIR_CODE_LENGTH_ / 2 ) + 1 To 0 Step -2
293
+ code(i) = Mid(CODE_ALPHABET_, 1 + doubleMod(lat, ENCODING_BASE_), 1 )
294
+ code(i + 1 ) = Mid(CODE_ALPHABET_, 1 + doubleMod(lng, ENCODING_BASE_), 1 )
295
+ lat = Int(lat / ENCODING_BASE_)
296
+ lng = Int(lng / ENCODING_BASE_)
297
+ Next
298
+ Dim finalCodeLen As Integer
299
+ finalCodeLen = codeLength
300
+ ' Add padding characters if necessary.
301
+ If codeLength < SEPARATOR_POSITION_ Then
302
+ For i = codeLength To SEPARATOR_POSITION_ - 1
303
+ code(i) = PADDING_CHARACTER_
304
+ Next
305
+ finalCodeLen = SEPARATOR_POSITION_
306
+ EndIf
307
+ ' Build the final code and return it.
308
+ Dim finalCode As String
309
+ For i = 0 To finalCodeLen
310
+ finalCode = finalCode & code(i)
311
+ Next
312
+ OLCEncode = finalCode
247
313
End Function
248
314
249
315
' Decodes an Open Location Code into an array of latlo, lnglo, latcenter, lngcenter, lathi, lnghi, codelength.
@@ -427,25 +493,6 @@ Private Function mergeCode(ByVal latCode As String, ByVal lngCode As String, ByV
427
493
mergeCode = code
428
494
End Function
429
495
430
- ' Encode a coordinate into an OLC sequence.
431
- Private Function encodeCoordinate (ByVal degrees As Double , ByVal digits As Integer ) As String
432
- Dim code As String
433
- Dim remaining, precision As Double
434
- Dim i As Integer
435
- code = ""
436
- remaining = degrees
437
- precision = CDbl(ENCODING_BASE_)
438
- For i = 1 To digits
439
- Dim digitValue As Double
440
- ' Get the latitude digit.
441
- digitValue = Int(remaining / precision)
442
- remaining = remaining - digitValue * precision
443
- code = code + Mid(CODE_ALPHABET_, digitValue + 1 , 1 )
444
- precision = precision / ENCODING_BASE_
445
- Next
446
- encodeCoordinate = code
447
- End Function
448
-
449
496
' Decode an OLC code made up of lat/lng pairs.
450
497
Private Function decodePairs (code) As OLCArea
451
498
Dim lat, lng, precision As Double
@@ -478,34 +525,6 @@ Private Function decodePairs(code) As OLCArea
478
525
decodePairs = codeArea
479
526
End Function
480
527
481
- ' Encode a location using the grid refinement method into an OLC string.
482
- ' The grid refinement method divides the area into a grid of 4x5, and uses a
483
- ' single character to refine the area. This allows default accuracy OLC codes
484
- ' to be refined with just a single character.
485
- ' This algorithm is used for codes longer than 10 digits.
486
- Private Function encodeGrid (ByVal latitude As Double , ByVal longitude As Double , ByVal codeLength As Integer ) As String
487
- Dim code As String
488
- Dim latPlaceValue, lngPlaceValue, lat, lng As Double
489
- Dim i, row, col As Integer
490
- code = ""
491
- latPlaceValue = CDbl(GRID_SIZE_DEGREES_)
492
- lngPlaceValue = CDbl(GRID_SIZE_DEGREES_)
493
- ' Adjust latitude and longitude so they fall into positive ranges and get the offset for the required places.
494
- lat = doubleMod(latitude + LATITUDE_MAX_, latPlaceValue)
495
- lng = doubleMod(longitude + LONGITUDE_MAX_, lngPlaceValue)
496
- For i = 1 To codeLength
497
- ' Work out the row and column.
498
- row = Int(lat / (latPlaceValue / GRID_ROWS_))
499
- col = Int(lng / (lngPlaceValue / GRID_COLUMNS_))
500
- latPlaceValue = latPlaceValue / GRID_ROWS_
501
- lngPlaceValue = lngPlaceValue / GRID_COLUMNS_
502
- lat = lat - row * latPlaceValue
503
- lng = lng - col * lngPlaceValue
504
- code = code + Mid(CODE_ALPHABET_, row * GRID_COLUMNS_ + col + 1 , 1 )
505
- Next
506
- encodeGrid = code
507
- End Function
508
-
509
528
' Decode the grid refinement portion of an OLC code.
510
529
Private Function decodeGrid (ByVal code As String ) As OLCArea
511
530
Dim gridOffSet As OLCArea
@@ -542,15 +561,6 @@ Private Function doubleMax(ByVal number1 As Double, ByVal number2 As Double) As
542
561
End If
543
562
End Function
544
563
545
- ' Provide a min function.
546
- Private Function doubleMin (ByVal number1 As Double , ByVal number2 As Double ) As Double
547
- If number1 < number2 Then
548
- doubleMin = number1
549
- Else
550
- doubleMin = number2
551
- End If
552
- End Function
553
-
554
564
' Provide an ABS function for doubles.
555
565
Private Function doubleABS (ByVal number As Double ) As Double
556
566
If number < 0 Then
@@ -630,17 +640,17 @@ Sub TestOLCLibrary()
630
640
encodingCodes(3 ) = "7FG49QCJ+2VXGJ"
631
641
encodingCoordinates(3 ) = Array(20.3701135 , 2.78223535156 , 20.370113 , 2.782234375 , 20.370114 , 2.78223632813 )
632
642
encodingCodes(4 ) = "8FVC2222+22"
633
- encodingCoordinates(4 ) = Array(47.0000625 , 8.0000625 , 47 # , 8 # , 47.000125 , 8.000125 )
643
+ encodingCoordinates(4 ) = Array(47.0000625 , 8.0000625 , 47 , 8 , 47.000125 , 8.000125 )
634
644
encodingCodes(5 ) = "4VCPPQGP+Q9"
635
645
encodingCoordinates(5 ) = Array(-41.2730625 , 174.7859375 , -41.273125 , 174.785875 , -41.273 , 174.786 )
636
646
encodingCodes(6 ) = "62G20000+"
637
- encodingCoordinates(6 ) = Array(0.5 , -179.5 , 0 # , -180 # , 1 , -179 )
647
+ encodingCoordinates(6 ) = Array(0.5 , -179.5 , 0 , -180 , 1 , -179 )
638
648
encodingCodes(7 ) = "22220000+"
639
649
encodingCoordinates(7 ) = Array(-89.5 , -179.5 , -90 , -180 , -89 , -179 )
640
650
encodingCodes(8 ) = "7FG40000+"
641
- encodingCoordinates(8 ) = Array(20.5 , 2.5 , 20 # , 2 # , 21 # , 3 #)
651
+ encodingCoordinates(8 ) = Array(20.5 , 2.5 , 20 , 2 , 21 , 3 #)
642
652
encodingCodes(9 ) = "22222222+22"
643
- encodingCoordinates(9 ) = Array(-89.9999375 , -179.9999375 , -90 # , -180 # , -89.999875 , -179.999875 )
653
+ encodingCoordinates(9 ) = Array(-89.9999375 , -179.9999375 , -90 , -180 , -89.999875 , -179.999875 )
644
654
encodingCodes(10 ) = "6VGX0000+"
645
655
encodingCoordinates(10 ) = Array(0.5 , 179.5 , 0 , 179 , 1 , 180 )
646
656
encodingCodes(11 ) = "CFX30000+"
@@ -720,6 +730,5 @@ Sub TestOLCLibrary()
720
730
Exit Sub
721
731
End If
722
732
723
-
724
733
MsgBox ("All tests pass" )
725
734
End Sub
0 commit comments