Skip to content

Instantly share code, notes, and snippets.

@janbaykara
Last active June 8, 2020 15:03
Show Gist options
  • Save janbaykara/1e982b6af957c3e2b13849960799d2df to your computer and use it in GitHub Desktop.
Save janbaykara/1e982b6af957c3e2b13849960799d2df to your computer and use it in GitHub Desktop.
Postcodes.io API batch consumer
(async () => {
const postcodeQueries = await bulk_postcode_geo(data.map(d => d.postcode).filter(Boolean))
})()
import fetch from 'node-fetch'
function chunk_array<T>(array: T[], chunk: number): T[] {
var i,j,temparray;
var arrayOfArrays = []
for (i=0,j=array.length; i<j; i+=chunk) {
temparray = array.slice(i,i+chunk);
arrayOfArrays.push(temparray)
}
return arrayOfArrays
}
function batch_and_aggregate(arr_limit) {
return function decorator <OFn extends (...args: any) => any>(original_fn: OFn) {
return async function resulting_fn(arr) {
var batches = chunk_array(arr, arr_limit)
const results = await Promise.all<ReturnType<OFn>>(batches.map(batch => original_fn(batch)))
return results.reduce((arr, res) => arr.concat(res), [])
}
}
}
// const normalise_postcode = (str: string) => str.trim().replace(/ \s+/mig, '').toUpperCase()
export const bulk_postcode_geo = batch_and_aggregate(100)(async (postcodes) => {
// var postcodes = postcodes.map(normalise_postcode)
var response = await fetch('https://api.postcodes.io/postcodes', {
method: "POST",
body: JSON.stringify({ postcodes }),
headers: { 'Content-Type': 'application/json' }
})
var status = response.status
var data = await response.json() as PostcodeBatchResult
var result = data.result
if (status !== 200 || !result) {
console.log(status, data, result)
throw new Error(`Failed to bulk geocode ${postcodes}`)
}
return result
})
//
export interface PostcodeBatchResult {
status: number;
result: ResultElement[];
}
export interface ResultElement {
query: string;
result: ResultResult;
}
export interface ResultResult {
postcode: string;
quality: number;
eastings: number;
northings: number;
country: string;
nhs_ha: string;
longitude: number;
latitude: number;
european_electoral_region: string;
primary_care_trust: string;
region: string;
lsoa: string;
msoa: string;
incode: string;
outcode: string;
parliamentary_constituency: string;
admin_district: string;
parish: string;
admin_county: null | string;
admin_ward: string;
ced: null | string;
ccg: string;
nuts: string;
codes: Codes;
}
export interface Codes {
admin_district: string;
admin_county: string;
admin_ward: string;
parish: string;
parliamentary_constituency: string;
ccg: string;
ccg_id: string;
ced: string;
nuts: string;
}
@janbaykara
Copy link
Author

janbaykara commented Jun 8, 2020

Fuzzing logic

const [latitude, longitude] = offsetCoordinates([result.latitude, result.longitude], 500)
export const offsetCoordinates = (input: [number, number], offsetMeters = 100) => {
  const [lat, lon] = input
 //Earth’s radius, sphere
 const R=6378137

 //offsets in meters
 const direction = Math.random() > 0.5 ? 1 : -1
 const latitudeLean = Math.random()
 const longitudeLean = 1 - latitudeLean
 const dn = offsetMeters * latitudeLean * direction
 const de = offsetMeters * longitudeLean * direction

 //Coordinate offsets in radians
 const dLat = dn / R
 const dLon = de / (R * Math.cos (Math.PI *lat/180))

 //OffsetPosition, decimal degrees
 const latO = lat + dLat * 180/Math.PI
 const lonO = lon + dLon * 180/Math.PI 
 const output = [latO, lonO]
 return output
}

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