-
-
Save kriscarle/15da1f0d38406589dc0b1bf140f79dd3 to your computer and use it in GitHub Desktop.
Airtable Shapefile Import
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
const DEBUG = false // set true to print debugging output | |
// load imports | |
try { | |
let importsTable = base.getTable("IMPORTS"); | |
let imports = await importsTable.selectRecordsAsync(); | |
for (let imp of imports.records) { | |
let code = imp.getCellValue("Code") | |
eval(code) | |
} | |
} catch (err) { | |
output.markdown('## Error: This script requires a special JavaScript imports table be included in the Base') | |
throw new Error('missing imports') | |
} | |
// get the Shapefile from user | |
let fileResult = await input.fileAsync( | |
'Import a Shapefile (.zip)', | |
{ | |
allowedFileTypes: ['.zip'] | |
} | |
); | |
output.text(`You uploaded: '${fileResult.file.name}'`); | |
output.markdown('# Loading Data ....') | |
// get the file as a buffer | |
const fileAsBlob = new Blob([fileResult.file]); | |
var buffer = await fileAsBlob.arrayBuffer() | |
// unzip the zip file | |
let zip = await JSZip.loadAsync(buffer); | |
// get all files in the zip | |
const files = await zip.file(/.+/); | |
const fileData = await Promise.all(files.map(async (file) => { | |
let data | |
let ext = file.name.slice(-3).toLowerCase() | |
if (ext === 'shp' || ext === 'dbf') { | |
data = Buffer.Buffer.from(await file.async("arraybuffer")); | |
} | |
else { | |
data = await file.async("text"); | |
} | |
return { | |
name: file.name, | |
ext: ext, | |
data: data | |
} | |
})); | |
output.clear() | |
// finished unzipping | |
let shp = {} | |
let dbf = {} | |
let prj = {} | |
let cpg = {} | |
fileData.forEach( file => { | |
if (file.ext === 'shp') { | |
shp = file | |
} else if (file.ext === 'prj') { | |
if (DEBUG) output.text(`projection: ${file.data}`) | |
prj = file | |
prj.data = proj4(file.data) | |
} else if (file.ext === 'dbf') { | |
dbf = file | |
} else if (file.ext === 'cpg') { | |
cpg = file | |
} | |
}) | |
let error | |
if (!shp || !prj) { | |
output.markdown('# Error: No Shapefile Found, missing .shp') | |
error = true | |
} | |
if (!prj) { | |
output.markdown('# Error: Missing projection file, .prj') | |
error = true | |
} | |
if (!dbf || !cpg) { | |
output.markdown('# Error: Incomplete Shapefile, missing .DBF or .CPG') | |
error = true | |
} | |
if (!error) { | |
const parsedProperties = ParseDBF(dbf.data, cpg.data); | |
if (DEBUG) output.inspect(parsedProperties) | |
const parsedShapes = ParseShp(shp.data, prj.data) | |
if (DEBUG) output.inspect(parsedShapes) | |
const geojson = { | |
type: 'FeatureCollection', | |
features: [] | |
} | |
for(var i = 0; i < parsedShapes.length; i++) { | |
geojson.features.push({ | |
'type': 'Feature', | |
'geometry': parsedShapes[i], | |
'properties': parsedProperties[i] | |
}) | |
} | |
if (DEBUG) output.inspect(geojson) | |
output.markdown(`### Found ${geojson.features.length} records`) | |
const firstFeature = geojson.features[0] | |
output.markdown('Select the table where data will be imported') | |
output.markdown('The table must have the following columns: Name (text), Notes (long text), Location (text), Cache (text)') | |
let selectedTable = await input.tableAsync('Select Table'); | |
output.clear() | |
const props = Object.keys(firstFeature.properties) | |
output.markdown('Choose which attribute from the data will be used as the Name') | |
let nameField = await input.buttonsAsync('Name Field?', props); | |
output.clear() | |
// split data into 50 records blocks for bulk import | |
const blocks = [] | |
for (var i = 0; i < geojson.features.length; i += 50) { | |
let start = i | |
let end = i + 50 | |
if (end > geojson.features.length) end = geojson.features.length | |
const block = geojson.features.slice(start, end) | |
blocks.push(block) | |
} | |
// insert each block | |
await Promise.all(blocks.map(block => { | |
const records = block.map(feature => { | |
let featureCentroid = firstFeature | |
if (firstFeature.geometry.type !== 'Point') { | |
// convert polygons to centroids | |
featureCentroid = turf.centroid(feature) | |
} | |
// get coords | |
let coords | |
if (featureCentroid) { | |
coords = featureCentroid.geometry.coordinates | |
} | |
return { | |
fields: { | |
"Name": feature.properties[nameField], | |
"Notes": JSON.stringify(feature.properties), | |
"Location": coords ? `${coords[1]},${coords[0]}`: "" | |
} | |
} | |
}) | |
return selectedTable.createRecordsAsync(records) | |
})) | |
output.markdown('### Import Complete! You can now setup a Map Block') | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment