@@ -107,22 +107,31 @@ pub fn is_full(code: &str) -> bool {
107
107
pub fn encode ( pt : Point < f64 > , code_length : usize ) -> String {
108
108
let ( lng, lat) = pt. x_y ( ) ;
109
109
110
- let mut lat = clip_latitude ( lat) ;
111
- let lng = normalize_longitude ( lng) ;
112
-
113
110
let trimmed_code_length = min ( max ( code_length, MIN_CODE_LENGTH ) , MAX_CODE_LENGTH ) ;
114
111
115
- // Latitude 90 needs to be adjusted to be just less, so the returned code
116
- // can also be decoded.
117
- if lat > LATITUDE_MAX || ( LATITUDE_MAX - lat) < 1e-10f64 {
118
- lat -= compute_latitude_precision ( trimmed_code_length) ;
112
+ // This approach converts each value to an integer after multiplying it by the final precision.
113
+ // This allows us to use only integer operations, so avoiding any accumulation of floating
114
+ // point representation errors.
115
+
116
+ // Convert latitude into a positive integer clipped into the range 0-(just under 180*2.5e7).
117
+ // Latitude 90 needs to be adjusted to be just less, so the returned code can also be decoded.
118
+ let mut lat_val = ( lat * LAT_INTEGER_MULTIPLIER as f64 ) . round ( ) as i64 ;
119
+ lat_val += LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER ;
120
+ if lat_val < 0 {
121
+ lat_val = 0
122
+ } else if lat_val >= 2 * LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER {
123
+ lat_val = 2 * LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER - 1 ;
119
124
}
120
125
121
- // Convert to integers.
122
- let mut lat_val =
123
- ( ( ( lat + LATITUDE_MAX ) * LAT_INTEGER_MULTIPLIER as f64 * 1e6 ) . round ( ) / 1e6f64 ) as i64 ;
124
- let mut lng_val =
125
- ( ( ( lng + LONGITUDE_MAX ) * LNG_INTEGER_MULTIPLIER as f64 * 1e6 ) . round ( ) / 1e6f64 ) as i64 ;
126
+ // Convert longitude into a positive integer and normalise it into the range 0-360*8.192e6.
127
+ let mut lng_val = ( lng * LNG_INTEGER_MULTIPLIER as f64 ) . round ( ) as i64 ;
128
+ lng_val += LONGITUDE_MAX as i64 * LNG_INTEGER_MULTIPLIER ;
129
+ if lng_val < 0 {
130
+ lng_val = lng_val % ( 2 * LONGITUDE_MAX as i64 * LNG_INTEGER_MULTIPLIER )
131
+ + ( 2 * LONGITUDE_MAX as i64 * LNG_INTEGER_MULTIPLIER )
132
+ } else if lng_val >= 2 * LONGITUDE_MAX as i64 * LNG_INTEGER_MULTIPLIER {
133
+ lng_val = lng_val % ( 2 * LONGITUDE_MAX as i64 * LNG_INTEGER_MULTIPLIER )
134
+ }
126
135
127
136
// Compute the code digits. This largely ignores the requested length - it
128
137
// generates either a 10 digit code, or a 15 digit code, and then truncates
0 commit comments