digipin/
encode.rs

1// Copyright (c) 2026 Pranam
2// ORCID: https://orcid.org/0009-0007-9316-3616
3//
4// This code is licensed under the Apache License, Version 2.0.
5//
6// You may obtain a copy of the License at
7//
8//     https://www.apache.org/licenses/LICENSE-2.0
9//
10// QR Code:
11// ![QR Code](/ORCID.png)
12// Scan the QR code to access my ORCID profile.
13
14use crate::constants::*;
15use crate::errors::DigipinError;
16use crate::location::Location;
17use crate::validation::{validate_digipin_territory, validate_geodetic_coordinates};
18
19/// Encodes coordinates into a canonical DIGIPIN.
20/// # Arguments
21/// - `location`: A struct containing the latitude and longitude to encode.
22/// # Returns
23/// - `Ok(String)`: A string representing the encoded DIGIPIN.
24/// - `Err(DigipinError)`: An error if the coordinates are invalid or outside the supported territory.
25pub fn encode(location: Location) -> Result<String, DigipinError> {
26    validate_geodetic_coordinates(location)?;
27    validate_digipin_territory(location)?;
28
29    let mut latitude_min = DIGIPIN_LATITUDE_MIN;
30    let mut latitude_max = DIGIPIN_LATITUDE_MAX;
31    let mut longitude_min = DIGIPIN_LONGITUDE_MIN;
32    let mut longitude_max = DIGIPIN_LONGITUDE_MAX;
33
34    let mut digipin = String::with_capacity(DIGIPIN_LENGTH);
35
36    for _ in 0..DIGIPIN_LENGTH {
37        let latitude_step = (latitude_max - latitude_min) / DIGIPIN_GRID_SIZE as f64;
38        let longitude_step = (longitude_max - longitude_min) / DIGIPIN_GRID_SIZE as f64;
39
40        let mut row = ((latitude_max - location.latitude) / latitude_step).floor() as usize;
41        let mut column = ((location.longitude - longitude_min) / longitude_step).floor() as usize;
42
43        // Clamp row and column to valid grid bounds
44        row = row.min(DIGIPIN_GRID_SIZE - 1);
45        column = column.min(DIGIPIN_GRID_SIZE - 1);
46
47        let symbol = DIGIPIN_LABEL_GRID[row][column];
48        digipin.push(symbol);
49
50        latitude_max -= row as f64 * latitude_step;
51        latitude_min = latitude_max - latitude_step;
52
53        longitude_min += column as f64 * longitude_step;
54        longitude_max = longitude_min + longitude_step;
55    }
56
57    Ok(digipin)
58}