Last active
April 3, 2023 22:49
-
-
Save trae410/14934b5df041a87082f3705a8940a370 to your computer and use it in GitHub Desktop.
Location privacy by using the hash values of rounded lat, lng coordinates
This file contains 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
// this is all front end | |
function getHash(string) { | |
function str2ab(str) { | |
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char | |
var bufView = new Uint16Array(buf); | |
for (var i=0, strLen=str.length; i < strLen; i++) { | |
bufView[i] = str.charCodeAt(i); | |
} | |
return buf; | |
} | |
var crypto = window.crypto; | |
var buffer = str2ab(string); // Fix | |
if (!crypto.subtle) { | |
throw new Error("crypto.subtle undefined are you on an https url?") | |
} | |
var hash_bytes = crypto.subtle.digest("SHA-256", buffer); | |
return hash_bytes.then(v => [...new Uint8Array(v)].map(x => x.toString(16).padStart(2, '0')).join('')) | |
} | |
const roundCoordinates = (coordinates, interval) => { | |
return coordinates.map(n => { | |
const numDivided = n / 10 | |
const dec = numDivided / interval | |
const rounded = Math.round(dec) | |
let nearest = rounded * interval * 10 | |
nearest = Math.round(nearest * 10000) / 10000 | |
return nearest | |
}) | |
} | |
const postLocation = async (coordinates, invoiceId, uniqueUserDeviceNumber, interval = 0.000025) => { | |
// interval must always = 0.000025 | |
// 47.1785,122.9572 round to 47.1785,122.9573 | |
// caveat : would have to redo all location hashes to change interval from 0.000025 to 0.00002 | |
const roundedCords = roundCoordinates(coordinates, interval) | |
console.log({coordinates, roundedCords}) | |
const coordinatesString = JSON.stringify(roundedCords) | |
const hash = await getHash(`${coordinatesString}${uniqueUserDeviceNumber}`) | |
const doc = { | |
id: hash, | |
// timestamp: Math.round(Date.now() / 1000), | |
invoices: [invoiceId] // should be firebase.firestore.FieldValue.arrayUnion(invoiceId) | |
} | |
return doc | |
} | |
const checkLocationsMatch = async ({currentLocation, interval = 0.000025, triggerLocations, uniqueUserDeviceNumber}) => { | |
// the interval used 0.000025 will round to neared multiple of ... | |
const roundedCords = roundCoordinates(currentLocation, interval) | |
const possibleMatchSrting = JSON.stringify(roundedCords) | |
const hash = await getHash(`${possibleMatchSrting}${uniqueUserDeviceNumber}`) | |
const match = triggerLocations[hash] | |
return match | |
} | |
const getNearbyCoordinates = (coords, northSouth, eastWest) => { | |
// eastWest and northSouth create a rectangle | |
// problem: coordinates are rounded a lot so location could be pretty far off | |
// better to have the user select squares on a grid that are 0.000025 by 0.000025 or 0.00005?... | |
const interval = 0.000025 | |
const round = (n) => { | |
const numDivided = n / 10 | |
const dec = numDivided / interval | |
const rounded = Math.round(dec) | |
let nearest = rounded * interval * 10 | |
nearest = Math.round(nearest * 10000) / 10000 | |
return nearest | |
} | |
const meterToLat = 111111 | |
const meterToLng = 111111 * Math.cos(coords[0]) | |
const minLat = round(coords[0] - ((northSouth / 2) / meterToLat)) | |
const maxLat = round(coords[0] + ((northSouth / 2) / meterToLat)) | |
const minLng = round(coords[1] - ((eastWest / 2) / meterToLng)) | |
const maxLng = round(coords[1] + ((eastWest / 2) / meterToLng)) | |
const rangeLat = Math.abs(maxLat - minLat) | |
const rangeLng = Math.abs(maxLng - minLng) | |
// todo: figure out the right amount of iterations, maybe divide by interval * 2 | |
const latIterations = Math.ceil(rangeLat / interval) | |
const lngIterations = Math.ceil(rangeLng / interval) | |
let coordinatesArray = [] | |
let allLats = [minLat] | |
// get all lat iterations | |
for (let i=0; i<latIterations; i++) { | |
const currentLat = allLats[allLats.length - 1] | |
const newLat = Math.round((currentLat + interval) * 1000000) / 1000000 | |
if (!allLats.includes(newLat)) { | |
allLats.push(newLat) | |
} | |
} | |
let allLngs = [minLng] | |
for (let i=0; i<lngIterations; i++) { | |
const currentLng = allLngs[allLngs.length - 1] | |
const newLng = Math.round((currentLng + interval) * 1000000) / 1000000 | |
if (!allLngs.includes(newLng)) { | |
allLngs.push(newLng) | |
} | |
} | |
allLngs.forEach(lng => { | |
coordinatesArray.push( | |
...allLats.map(lat => { | |
return [lat, lng] | |
}) | |
) | |
}) | |
const roundedCoordinatesArray = [] | |
coordinatesArray.forEach(c => { | |
const rounded = [round(c[0]),round(c[1])] | |
const found = roundedCoordinatesArray.find(coord => coord[0] === rounded[0] && coord[1] === rounded[1]) | |
if (!found) { | |
roundedCoordinatesArray.push(rounded) | |
} | |
}) | |
return roundedCoordinatesArray | |
} | |
const postLocations = async ({coordinates, invoiceId, jobSiteNorthSouthDimension, jobSiteEastWestDimension, uniqueUserDeviceNumber}) => { | |
// add in a range of coordinates | |
const coordinatesSelected = getNearbyCoordinates(coordinates, jobSiteNorthSouthDimension, jobSiteEastWestDimension) | |
let triggerLocations = {} | |
console.log({coordinatesSelected}) | |
for (let i=0; i<coordinatesSelected.length; i++) { | |
const coordinate = coordinatesSelected[i] | |
let l = await postLocation(coordinate, invoiceId, uniqueUserDeviceNumber, 0.000025) | |
triggerLocations[l.id] = l | |
} | |
console.log({triggerLocations}) | |
return triggerLocations // would be a post request here | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To run functions in browser console: