Created
June 8, 2025 08:50
-
-
Save answerquest/7cc8da95ecb36c327df7be48f4484cb7 to your computer and use it in GitHub Desktop.
Python implementation of India's DigiPIN geocoding address system
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# adapted from https://github.com/CEPT-VZG/digipin/blob/main/src/digipin.js | |
import re | |
import math | |
REGEX_PATTERN_DIGIPIN = re.compile(r'^[2-9CFJKLMPT]{3}-[2-9CFJKLMPT]{3}-[2-9CFJKLMPT]{4}$') | |
REGEX_PATTERN_DIGIPIN_SEARCH = re.compile(r'[2-9CFJKLMPT]{3}-[2-9CFJKLMPT]{3}-[2-9CFJKLMPT]{4}') | |
DIGIPIN_GRID = [ | |
['F', 'C', '9', '8'], | |
['J', '3', '2', '7'], | |
['K', '4', '5', '6'], | |
['L', 'M', 'P', 'T'] | |
] | |
DIGIPIN_BOUNDS = { | |
'minLat': 2.5, | |
'maxLat': 38.5, | |
'minLon': 63.5, | |
'maxLon': 99.5 | |
} | |
def validate_digipin(d): | |
"""Validates if a given string is a valid digipin. Should be in format like: 4FP-994-M7C6""" | |
global REGEX_PATTERN_DIGIPIN | |
return bool(REGEX_PATTERN_DIGIPIN.match(d)) | |
def extract_digipin(d): | |
"""Finds first digipin in a larger string and extracts it.""" | |
global REGEX_PATTERN_DIGIPIN_SEARCH | |
match = REGEX_PATTERN_DIGIPIN_SEARCH.search(d) | |
if match: | |
return match.group(0) | |
return None | |
def get_digipin(lat, lon): | |
"""analogous to `function getDigiPin` in https://github.com/CEPT-VZG/digipin/blob/main/src/digipin.js""" | |
if lat < DIGIPIN_BOUNDS['minLat'] or lat > DIGIPIN_BOUNDS['maxLat']: | |
raise ValueError('Latitude out of range') | |
if lon < DIGIPIN_BOUNDS['minLon'] or lon > DIGIPIN_BOUNDS['maxLon']: | |
raise ValueError('Longitude out of range') | |
min_lat = DIGIPIN_BOUNDS['minLat'] | |
max_lat = DIGIPIN_BOUNDS['maxLat'] | |
min_lon = DIGIPIN_BOUNDS['minLon'] | |
max_lon = DIGIPIN_BOUNDS['maxLon'] | |
digipin = '' | |
for level in range(1, 11): | |
lat_div = (max_lat - min_lat) / 4 | |
lon_div = (max_lon - min_lon) / 4 | |
# REVERSED row logic (to match original) | |
row = 3 - math.floor((lat - min_lat) / lat_div) | |
col = math.floor((lon - min_lon) / lon_div) | |
row = max(0, min(row, 3)) | |
col = max(0, min(col, 3)) | |
digipin += DIGIPIN_GRID[row][col] | |
if level == 3 or level == 6: | |
digipin += '-' | |
# Update bounds (reverse logic for row) | |
max_lat = min_lat + lat_div * (4 - row) | |
min_lat = min_lat + lat_div * (3 - row) | |
min_lon = min_lon + lon_div * col | |
max_lon = min_lon + lon_div | |
return digipin | |
def get_latlng_from_digipin(digipin): | |
"""analogous to `function getLatLngFromDigiPin` in https://github.com/CEPT-VZG/digipin/blob/main/src/digipin.js, | |
But slight difference in output, this one gives simpler 2 values lat, lon in response instead of making a json array. | |
""" | |
if not validate_digipin(digipin): | |
raise ValueError('Invalid DIGIPIN') | |
pin = digipin.replace('-', '') | |
if len(pin) != 10: | |
raise ValueError('Invalid DIGIPIN') | |
min_lat = DIGIPIN_BOUNDS['minLat'] | |
max_lat = DIGIPIN_BOUNDS['maxLat'] | |
min_lon = DIGIPIN_BOUNDS['minLon'] | |
max_lon = DIGIPIN_BOUNDS['maxLon'] | |
for i in range(10): | |
char = pin[i] | |
found = False | |
ri = -1 | |
ci = -1 | |
# Locate character in DIGIPIN grid | |
for r in range(4): | |
for c in range(4): | |
if DIGIPIN_GRID[r][c] == char: | |
ri = r | |
ci = c | |
found = True | |
break | |
if found: | |
break | |
if not found: | |
raise ValueError('Invalid character in DIGIPIN') | |
lat_div = (max_lat - min_lat) / 4 | |
lon_div = (max_lon - min_lon) / 4 | |
lat1 = max_lat - lat_div * (ri + 1) | |
lat2 = max_lat - lat_div * ri | |
lon1 = min_lon + lon_div * ci | |
lon2 = min_lon + lon_div * (ci + 1) | |
# Update bounding box for next level | |
min_lat = lat1 | |
max_lat = lat2 | |
min_lon = lon1 | |
max_lon = lon2 | |
center_lat = round((min_lat + max_lat) / 2, 6) | |
center_lon = round((min_lon + max_lon) / 2, 6) | |
return center_lat, center_lon |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment