UNPKG

digipinjs

Version:

A comprehensive TypeScript library for encoding and decoding Indian geographic coordinates into DIGIPIN format (Indian Postal Digital PIN system). Features CLI tools, caching, batch processing, and Express middleware for seamless integration.

122 lines (121 loc) 4.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BOUNDS = void 0; exports.getDigiPin = getDigiPin; exports.getLatLngFromDigiPin = getLatLngFromDigiPin; const cache_1 = require("./cache"); const errors_1 = require("./errors"); const util_1 = require("./util"); const DIGIPIN_GRID = [ ['F', 'C', '9', '8'], ['J', '3', '2', '7'], ['K', '4', '5', '6'], ['L', 'M', 'P', 'T'], ]; const BOUNDS = { minLat: 2.5, maxLat: 38.5, minLon: 63.5, maxLon: 99.5 }; exports.BOUNDS = BOUNDS; const CHAR_TO_COORD = new Map(); for (let row = 0; row < DIGIPIN_GRID.length; row++) { for (let col = 0; col < DIGIPIN_GRID[row].length; col++) { CHAR_TO_COORD.set(DIGIPIN_GRID[row][col], { row, col }); } } function ensureBounds(lat, lon) { if (lat < BOUNDS.minLat || lat > BOUNDS.maxLat || lon < BOUNDS.minLon || lon > BOUNDS.maxLon) { throw new errors_1.BoundsError(lat, lon, BOUNDS); } } function formatPin(pin, format) { if (format === 'compact') { return pin.replace(/-/g, ''); } const compact = pin.replace(/-/g, ''); return `${compact.slice(0, 3)}-${compact.slice(3, 6)}-${compact.slice(6)}`; } function roundCoordinate(value, roundTo) { if (roundTo === 'none') { return value; } const decimals = typeof roundTo === 'number' ? roundTo : 6; const factor = 10 ** decimals; return Math.round(value * factor) / factor; } /** * Encode latitude & longitude into DIGIPIN. */ function getDigiPin(lat, lon, options = {}) { const { format = 'hyphenated', roundTo = 6, useCache = true } = options; ensureBounds(lat, lon); const roundedLat = roundCoordinate(lat, roundTo); const roundedLon = roundCoordinate(lon, roundTo); if (useCache) { const cached = (0, cache_1.getCachedEncode)(roundedLat, roundedLon, format); if (cached) { return cached; } } let minLat = BOUNDS.minLat; let maxLat = BOUNDS.maxLat; let minLon = BOUNDS.minLon; let maxLon = BOUNDS.maxLon; let code = ''; for (let level = 1; level <= 10; level++) { const latStep = (maxLat - minLat) / 4; const lonStep = (maxLon - minLon) / 4; let row = 3 - Math.floor((roundedLat - minLat) / latStep); let col = Math.floor((roundedLon - minLon) / lonStep); row = Math.min(3, Math.max(0, row)); col = Math.min(3, Math.max(0, col)); code += DIGIPIN_GRID[row][col]; maxLat = minLat + latStep * (4 - row); minLat = minLat + latStep * (3 - row); minLon = minLon + lonStep * col; maxLon = minLon + lonStep; } const formatted = formatPin(code, format); if (useCache) { (0, cache_1.setCachedEncode)(roundedLat, roundedLon, formatted, format); } return formatted; } /** * Decode DIGIPIN back to lat/lon center. */ function getLatLngFromDigiPin(pin, options = {}) { const { useCache = true } = options; const normalized = (0, util_1.normalizeDigiPin)(pin); if (useCache) { const cached = (0, cache_1.getCachedDecode)(normalized); if (cached) { return cached; } } let minLat = BOUNDS.minLat; let maxLat = BOUNDS.maxLat; let minLon = BOUNDS.minLon; let maxLon = BOUNDS.maxLon; for (const char of normalized) { const coord = CHAR_TO_COORD.get(char); if (!coord) { throw new errors_1.InvalidCharacterError(char); } const latStep = (maxLat - minLat) / 4; const lonStep = (maxLon - minLon) / 4; const newMaxLat = minLat + latStep * (4 - coord.row); const newMinLat = minLat + latStep * (3 - coord.row); const newMinLon = minLon + lonStep * coord.col; const newMaxLon = newMinLon + lonStep; minLat = newMinLat; maxLat = newMaxLat; minLon = newMinLon; maxLon = newMaxLon; } const result = { latitude: (minLat + maxLat) / 2, longitude: (minLon + maxLon) / 2, }; if (useCache) { (0, cache_1.setCachedDecode)(normalized, result); } return result; }