Skip to content

Instantly share code, notes, and snippets.

@trae410
Last active April 3, 2023 22:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trae410/14934b5df041a87082f3705a8940a370 to your computer and use it in GitHub Desktop.
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 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
}
@trae410
Copy link
Author

trae410 commented Apr 3, 2023

To run functions in browser console:


  const triggerLocations = await postLocations({
    coordinates: [49.1746, 123.9443],
    invoiceId: "asde344qt523",
    jobSiteNorthSouthDimension: 10, // length or width of the property meters
    jobSiteEastWestDimension: 10, // length or width of the property meters
    uniqueUserDeviceNumber: "10039204839201938", // a secret device number or something unique to the users device that is always the same if they are using the same device could also be an android userId or something
  })


// then at a new location see if location matches
// upon users current location changing...
  var match = await checkLocationsMatch({
    currentLocation: [49.1744, 123.9442], // s users current location
    interval: 0.000025, // interval not required default is 0.000025
    triggerLocations, // get them from a database or in this case they are stored globally
    uniqueUserDeviceNumber: "10039204839201938", // a secret device number or something unique to the users device that is always the same if they are using the same device could also be an android userId or something

  })
  // expected log should return a match
  console.log(`Match ${match ? "found" : "not found"}`, {hasMatch: match ? match : false})


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment