Help

Re: Add google static maps to an attachments field

4598 2
cancel
Showing results for 
Search instead for 
Did you mean: 
Z_Goddard
5 - Automation Enthusiast
5 - Automation Enthusiast

Add static Google Maps to attachments based on addresses in a table.

Source Code
/**
 * Copyright 2020 Bocoup
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * Airtable attach google static maps script
 *
 * This script looks at each row and adds google map images to an attachment
 * field cell in that row based on the addresses in the row. Rows with no
 * addresses or that already has attachments are skipped.
 *
 * This script uses Google's static map API. See for more info:
 * https://developers.google.com/maps/documentation/maps-static/intro
 *
 * **Notes on adapting this script.**
 *
 * Before running this script set the apiKey, tableName, addressFieldNames, and
 * mapTileAttachmentFieldName variables below. After that it can be run at any
 * time and it will update new records or old records with an empty attachments
 * cell. Further customizations can be made to the maps by modifying
 * staticmapOptions further down.
 */

'use strict';

// Google Maps API key
// See google's documentation to get a key.
// https://developers.google.com/maps/documentation/javascript/get-api-key
//
// The text of this script including the apiKey will be visible to everyone who
// can view this airtable base.
let apiKey = '**************';

// The name of the table this script will update.
let tableName = 'Map Table';
// Names of fields with an address to get a map for. Each field can have 0 or 1
// addresses.
let addressFieldNames = [
    'Map Address',
    // 'Start Address',
    // 'Special Address',
];
// Name of an attachments field that will be updated with static google map images.
let mapTileAttachmentFieldName = 'Map Attachments';

//
// After this line is the script logic. To use this script as-is, you do not need
// to edit anything after this point.
//

let RECORD_BATCH_COUNT = 50;

let table = base.getTable(tableName);
let allRecords = await table.selectRecordsAsync({
    // In the case there are a lot of fields, get the address and map tile fields only.
    fields: [...addressFieldNames, mapTileAttachmentFieldName],
});

let updates = [];

// Iterate every record in the table.
for (let recordId of allRecords.recordIds) {
    let record = allRecords.getRecord(recordId);

    // Skip records that have no addresses or already have attached images.
    if (
        !addressFieldNames.some(fieldName => record.getCellValue(fieldName)) ||
        record.getCellValue(mapTileAttachmentFieldName)
    ) {
        continue;
    }

    // Create google static map api urls from address fields for this record.
    let urls = [];
    for (let fieldName of addressFieldNames) {
        let staticmapOptions = {
            // The Google API Key. Required.
            key: apiKey,
            // The center of the returned map. Can be latitude, longitude
            // coordinates or an address. Required.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Locations
            center: record.getCellValue(fieldName),
            // Smaller numbers zoom out, larger numbers zoom in. Required.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Zoomlevels
            zoom: 17,

            // 640 by 640 pixels is the largest normal resolution at scale 1. Optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Imagesizes
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Largerimagesizes
            size: [640, 640],
            // Scale can be 1 or 2. Optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#scale_values
            scale: 1,
            // Which image format to return. Optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#ImageFormats
            format: 'jpg',
            // Maptype can be roadmap, terrain, hybrid, or streetview. Optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#MapTypes
            maptype: 'roadmap',
            // Language and region are optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#map-parameters
            language: 'en-US',
            region: 'us',

            // There are other options.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#feature-parameters
        };

        let staticmapParameters = Object.entries(staticmapOptions).map(([key, value]) => {
            if (key === 'size') {
                return `size=${value[0]}x${value[1]}`;
            }
            return `${key}=${encodeURIComponent(value)}`;
        });
        let staticmapUrl = `https://maps.googleapis.com/maps/api/staticmap?${staticmapParameters.join('&')}`;

        // Add a attachment cell object to urls to add to the record update.
        urls.push({url: staticmapUrl});
    }

    // Create record update with map urls.
    updates.push({id: record.id, fields: {[mapTileAttachmentFieldName]: urls}});
}

output.markdown(`Prepared ${updates.length} row updates out of ${allRecords.recordIds.length} in the table.`);

// For subsets of updates
for (let i = 0; i < updates.length; i += RECORD_BATCH_COUNT) {
    // Update table with a subset of all the updates ready to be made.
    output.markdown(`Applying update ${i + 1} through ${Math.min(i + RECORD_BATCH_COUNT, updates.length)} of ${updates.length}.`);
    await table.updateRecordsAsync(updates.slice(i, i + RECORD_BATCH_COUNT));
}

output.markdown('Done.');

This script looks at each row and adds google map images to an attachment field cell in that row based on the addresses in the row. Rows with no addresses or that already has attachments are skipped.

This script uses Google’s static map API. See for more info:

Notes on adapting this script.

Before running this script set the apiKey, tableName, addressFieldNames, and mapTileAttachmentFieldName variables below. After that it can be run at any time and it will update new records or old records with an empty attachments cell. Further customizations can be made to the maps by modifying staticmapOptions further down.

Made this to help do things like what is mentioned in Map in Page Designer?.

22 Replies 22
Aron
7 - App Architect
7 - App Architect

Love it! Could see this be super useful in so many ways, thanks for sharing Z_Goddard!

Eddie_Kaeser
6 - Interface Innovator
6 - Interface Innovator

New to Scripting… I used the Script successfully once, then decided to change the ZOOM level. I deleted the previous Attachments and ran the script again. The Script runs but the attachments are bad files. what am i doing wrong?

@Eddie_Kaeser what ZOOM level did you change it to?

  1. it actually is working now. guess I was impatient. Thanks for the Script. it works great and is extremely helpful!
Melissa_Bradley
6 - Interface Innovator
6 - Interface Innovator

Has anyone added map markers to the script? I see you can add markers in the Developer Guide, but I am new to scripting so a bit confused on how to add to the script.

Melissa_Bradley
6 - Interface Innovator
6 - Interface Innovator

I would also love to see this adapted to a street view image! I am using airtable for a plumbing company, so would be valuable to have a street view image of the house to add to job tickets.

@Z_Goddard Do you know how to add map markers to the script? Your script is fantastic for our use case, but not much help without map markers unfortunately. Thanks!

Hi @Melissa_Bradley,

You can change the script to add markers. One way to change it would be to replace center in the staticmapOptions object to markers with something like:

// Add markers to the map. This is a set of locations written as
// coordinates or addresses. Required.
// See for more info:
// https://developers.google.com/maps/documentation/maps-static/dev-guide#Markers
markers: ‘color:black|’ + record.getCellValue(fieldName).split(’\n’).join(’|’),

Here is the full script edited with that change. In this version it will the same configured field with a location per line to make maps with markers instead of just specifying the center.

Source Code with Markers change
/**
 * Copyright 2020 Bocoup
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * Airtable attach google static maps script
 *
 * This script looks at each row and adds google map images to an attachment
 * field cell in that row based on the addresses in the row. Rows with no
 * addresses or that already has attachments are skipped.
 *
 * This script uses Google's static map API. See for more info:
 * https://developers.google.com/maps/documentation/maps-static/intro
 *
 * **Notes on adapting this script.**
 *
 * Before running this script set the apiKey, tableName, addressFieldNames, and
 * mapTileAttachmentFieldName variables below. After that it can be run at any
 * time and it will update new records or old records with an empty attachments
 * cell. Further customizations can be made to the maps by modifying
 * staticmapOptions further down.
 */

'use strict';

// Google Maps API key
// See google's documentation to get a key.
// https://developers.google.com/maps/documentation/javascript/get-api-key
//
// The text of this script including the apiKey will be visible to everyone who
// can view this airtable base.
let apiKey = '**************';

// The name of the table this script will update.
let tableName = 'Map Table';
// Names of fields with an address to get a map for. Each field can have 0 or 1
// addresses.
let addressFieldNames = [
    'Map Address',
    // 'Start Address',
    // 'Special Address',
];
// Name of an attachments field that will be updated with static google map images.
let mapTileAttachmentFieldName = 'Map Attachments';

//
// After this line is the script logic. To use this script as-is, you do not need
// to edit anything after this point.
//

let RECORD_BATCH_COUNT = 50;

let table = base.getTable(tableName);
let allRecords = await table.selectRecordsAsync({
    // In the case there are a lot of fields, get the address and map tile fields only.
    fields: [...addressFieldNames, mapTileAttachmentFieldName],
});

let updates = [];

// Iterate every record in the table.
for (let recordId of allRecords.recordIds) {
    let record = allRecords.getRecord(recordId);

    // Skip records that have no addresses or already have attached images.
    if (
        !addressFieldNames.some(fieldName => record.getCellValue(fieldName)) ||
        record.getCellValue(mapTileAttachmentFieldName)
    ) {
        continue;
    }

    // Create google static map api urls from address fields for this record.
    let urls = [];
    for (let fieldName of addressFieldNames) {
        let staticmapOptions = {
            // The Google API Key. Required.
            key: apiKey,
            // Add markers to the map. This is a set of locations written as
            // coordinates or addresses. Required.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Markers
            markers: 'color:black|' + record.getCellValue(fieldName).split('\n').join('|'),
            // The center of the returned map. Can be latitude, longitude
            // coordinates or an address. Optional if markers is set.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Locations
            // center: '',
            // Smaller numbers zoom out, larger numbers zoom in. Required.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Zoomlevels
            zoom: 17,

            // 640 by 640 pixels is the largest normal resolution at scale 1. Optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Imagesizes
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#Largerimagesizes
            size: [640, 640],
            // Scale can be 1 or 2. Optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#scale_values
            scale: 1,
            // Which image format to return. Optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#ImageFormats
            format: 'jpg',
            // Maptype can be roadmap, terrain, hybrid, or streetview. Optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#MapTypes
            maptype: 'roadmap',
            // Language and region are optional.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#map-parameters
            language: 'en-US',
            region: 'us',

            // There are other options.
            // See for more info:
            // https://developers.google.com/maps/documentation/maps-static/dev-guide#feature-parameters
        };

        let staticmapParameters = Object.entries(staticmapOptions).map(([key, value]) => {
            if (key === 'size') {
                return `size=${value[0]}x${value[1]}`;
            }
            return `${key}=${encodeURIComponent(value)}`;
        });
        let staticmapUrl = `https://maps.googleapis.com/maps/api/staticmap?${staticmapParameters.join('&')}`;

        // Add a attachment cell object to urls to add to the record update.
        urls.push({url: staticmapUrl});
    }

    // Create record update with map urls.
    updates.push({id: record.id, fields: {[mapTileAttachmentFieldName]: urls}});
}

output.markdown(`Prepared ${updates.length} row updates out of ${allRecords.recordIds.length} in the table.`);

// For subsets of updates
for (let i = 0; i < updates.length; i += RECORD_BATCH_COUNT) {
    // Update table with a subset of all the updates ready to be made.
    output.markdown(`Applying update ${i + 1} through ${Math.min(i + RECORD_BATCH_COUNT, updates.length)} of ${updates.length}.`);
    await table.updateRecordsAsync(updates.slice(i, i + RECORD_BATCH_COUNT));
}

output.markdown('Done.');

This worked great!! Thank you so much!