Skip to content

Instantly share code, notes, and snippets.

@missinglink
Last active April 22, 2024 23:35
Show Gist options
  • Save missinglink/c534ebaab8a34e7150d0ee0521045682 to your computer and use it in GitHub Desktop.
Save missinglink/c534ebaab8a34e7150d0ee0521045682 to your computer and use it in GitHub Desktop.
Output a canonical normalised GeoJSON string which will hash deterministically
const geojson = {
'id': -99,
'type': -6,
'properties': -5,
'geometry': -4,
'coordinates': -3,
'bbox': -2
}
// GeoJSON Feature/Geometry
function isGeoJSON (path, ks) {
return (
(path === '$' && ['type', 'geometry'].every(k => ks.includes(k))) ||
(path === '$.geometry' && ['type', 'coordinates'].every(k => ks.includes(k)))
)
}
function sort (path, keys) {
let sorted = keys.sort()
// GeoJSON Feature/Geometry objects sort keys in custom order
if (isGeoJSON(path, keys.map(s => s[0]))) {
sorted = sorted.sort((a, b) => geojson[a[0]] - geojson[b[0]])
}
return sorted
}
function replacer (key, value, path) {
switch (Object.prototype.toString.call(value)) {
case '[object Object]': return Object.fromEntries(sort(path, Object.entries(value)))
case '[object String]': return value.normalize('NFKC')
case '[object Number]': return value % 1 === 0 ? value : Math.round(parseFloat(value) * 1e7) / 1e7
default: return value
}
}
function replacerWithPath (fn) {
let paths = new Map()
return function (key, value) {
let path = (paths.get(this) || '$')
if (key) path += Array.isArray(this) ? `[${key}]` : '.' + key
let v = fn(key, value, path)
if (v === Object(v)) paths.set(v, path)
return v
}
}
function squashArrays (str) {
return str.replace(/\[[^\[\]]+\]/g, arr => {
const squash = arr.replace(/\s\s+/g, ' ')
return squash.length <= 60 ? squash : arr
})
}
module.exports = (value) => squashArrays(JSON.stringify(value, replacerWithPath(replacer), 2))
// can be run directly
if (require.main === module) {
const json = JSON.parse(require('fs').readFileSync(process.argv[2], 'utf8'))
process.stdout.write(module.exports(json))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment