import { LocationV1 } from "../data/version1"; /** * This is utility class that help works with coordinates and convert their to QTH locators */ export class FireMapUtil { private static _appendLoc = '00AA00AA'; // mapping zoom to locator symbol private static _locatorSym = [10, 8, 6, 4, 2, 0]; public static composeTileLocByZoom(loc: string, zoom: number): string { let indexOfAppend = 10 - (zoom * 2); let fullLoc = loc.slice(0, indexOfAppend) + this._appendLoc.slice(indexOfAppend - 2); if (fullLoc == 'AA') { fullLoc += this._appendLoc; } return fullLoc.toUpperCase(); } /** * Rounds the locator up to a specific zoom. * @param loc QTH locator * @param zoom zoom value */ public static getLocByZoom(loc: string, zoom: number): string { return loc.slice(0, this._locatorSym[zoom]); } /** * Convert lat, long to QTH locator * @param location QTH locator with 10 symbols */ public static getLocator(location: LocationV1): string { const alphabet: string[] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); let word = ''; // first lvl AA-RR let lon = location.long + 180; let lat = location.lat + 90; let lon1 = lon / 20; let lat1 = lat / 10; word += alphabet[Math.trunc(lon1)] + alphabet[Math.trunc(lat1)]; // second lvl 00-99 let lonR = lon - (Math.trunc(lon1) * 20); let latR = lat - (Math.trunc(lat1) * 10); let lon2 = lonR / 2; let lat2 = latR / 1; word += Math.trunc(lon2).toString() + Math.trunc(lat2).toString(); // third lvl aa-xx lonR = lonR - (Math.trunc(lon2) * 2); latR = latR - (Math.trunc(lat2) * 1); let lon3 = lonR * 12; let lat3 = latR * 24; word += (alphabet[Math.trunc(lon3)] + alphabet[Math.trunc(lat3)]); // fourth lvl 00-99 lonR = lonR - (Math.trunc(lon3) / 12); latR = latR - (Math.trunc(lat3) / 24); let lon4 = lonR * 120 let lat4 = latR * 240 word += Math.trunc(lon4).toString() + Math.trunc(lat4).toString(); // fifth lvl AA-XX lonR = lonR - (Math.trunc(lon4) / 120); latR = latR - (Math.trunc(lat4) / 240); let lon5 = lonR * 2880; let lat5 = latR * 5760; word += alphabet[Math.trunc(lon5)] + alphabet[Math.trunc(lat5)]; // correct special chars word = word.toUpperCase(); let resultLoc: string = ''; let index = 0; for (let char of word) { // if is digit if (char.charCodeAt(0) >= '0'.charCodeAt(0) - 1 && char.charCodeAt(0) <= '9'.charCodeAt(0) + 1) { // check number chars of loc if (char.charCodeAt(0) - '0'.charCodeAt(0) >= 10) { char = '9'; } else if ('9'.charCodeAt(0) - char.charCodeAt(0) >= 10) { char = '0'; } } else { // if is char if (char.charCodeAt(0) >= 'A'.charCodeAt(0) - 1 && char.charCodeAt(0) <= 'X'.charCodeAt(0) + 1) { if (index == 0 || index == 1) { // check for first chars pair in loc if (char.charCodeAt(0) - 'A'.charCodeAt(0) >= 18) { char = 'R'; } else if ('R'.charCodeAt(0) - char.charCodeAt(0) >= 18) { char = 'A'; } } else { if (char.charCodeAt(0) - 'A'.charCodeAt(0) >= 24) { char = 'X'; } else if ('X'.charCodeAt(0) - char.charCodeAt(0) >= 24) { char = 'A'; } } } } resultLoc += char; index++; } return resultLoc; } /** * Calculates the positions of the extreme corners of the locator * * @param loc QTH locator * @param zoom symbol position inside locator (locator zoom) * @param invertAngles flag for returns left bottom and right top angles * @returns location array [from, to] (left top and right bottom) */ public static getLocatorCoordinates(loc: string, zoom: number, invertAngles: boolean = false): LocationV1[] { let symbol: number = this._locatorSym[zoom]; loc = loc.toUpperCase(); // if it smallest zoom if (symbol == 0) { if (invertAngles) return [{ lat: 90, long: 180 }, { lat: -90, long: -180 }]; else return [{ lat: 90, long: -180 }, { lat: -90, long: 180 }]; } let appendLoc = this._appendLoc.substring(symbol - 2); // calculate coordinates of corners let leftBottom = this.locatorToPolar( loc.slice(0, symbol - 2) + loc.slice(symbol - 2, symbol - 1) + loc.slice(symbol - 1, symbol) + appendLoc ); let rightBottom = this.locatorToPolar( loc.slice(0, symbol - 2) + String.fromCharCode(1 + loc[symbol - 2].charCodeAt(0)) + loc.slice(symbol - 1, symbol) + appendLoc ); let rightTop = this.locatorToPolar( loc.slice(0, symbol - 2) + String.fromCharCode(1 + loc[symbol - 2].charCodeAt(0)) + String.fromCharCode(1 + loc[symbol - 1].charCodeAt(0)) + appendLoc ) let leftTop = this.locatorToPolar( loc.slice(0, symbol - 2) + loc.slice(symbol - 2, symbol - 1) + String.fromCharCode(1 + loc[symbol - 1].charCodeAt(0)) + appendLoc ); if (invertAngles) { return [leftBottom, rightTop]; } return [leftTop, rightBottom]; } public static locatorToPolar(loc: string): LocationV1 { let o = 0; let e = new Array(); for (loc = loc.toUpperCase(); o < 10;) e[o] = loc.charCodeAt(o++) - 65; e[2] += 17; e[3] += 17; e[6] += 17; e[7] += 17; let long = 20 * e[0] + 2 * e[2] + e[4] / 12 + e[6] / 120 + e[8] / 2880 - 180 let lat = 10 * e[1] + e[3] + e[5] / 24 + e[7] / 240 + e[9] / 5760 - 90; return { lat: lat, long: long }; } public static calcCenterCoordinates(from: LocationV1, to: LocationV1): LocationV1 { let toRad = (c) => c * Math.PI / 180; let toDeegre = (c) => c * 180 / Math.PI; let flat = toRad(from.lat); let flng = toRad(from.long); let tlat = toRad(to.lat); let tlng = toRad(to.long); let deltaLong = tlng - flng; let xModified = Math.cos(tlat) * Math.cos(deltaLong); let yModified = Math.cos(tlat) * Math.sin(deltaLong); let centerLat = Math.atan2(Math.sin(flat) + Math.sin(tlat), Math.sqrt((Math.cos(flat) + xModified) * (Math.cos(flat) + xModified) + yModified * yModified)); let centerLong = flng + Math.atan2(yModified, Math.cos(flat) + xModified); centerLat = toDeegre(centerLat); centerLong = toDeegre(centerLong); return { lat: centerLat, long: centerLong }; } }