Skip to content

Instantly share code, notes, and snippets.

@kriscarle
Created May 4, 2020 22:22
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 kriscarle/15da1f0d38406589dc0b1bf140f79dd3 to your computer and use it in GitHub Desktop.
Save kriscarle/15da1f0d38406589dc0b1bf140f79dd3 to your computer and use it in GitHub Desktop.
Airtable Shapefile Import
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