Skip to content

Instantly share code, notes, and snippets.

@bindiego
Last active March 19, 2025 01:54
Show Gist options
  • Save bindiego/7023ad14cde266654d5f6c97fb2d8c03 to your computer and use it in GitHub Desktop.
Save bindiego/7023ad14cde266654d5f6c97fb2d8c03 to your computer and use it in GitHub Desktop.
Google Sheets formula with Google Maps API
/**
* Calculate the distance between two
* locations on Google Maps.
*
* =GOOGLEMAPS_DISTANCE("NY 10005", "Hoboken NJ", "walking")
*
* @param {String} origin The address of starting point
* @param {String} destination The address of destination
* @param {String} mode The mode of travel (driving, walking, bicycling or transit)
* @return {String} The distance in miles
* @customFunction
*/
const GOOGLEMAPS_DISTANCE = (origin, destination, mode) => {
const { routes: [data] = [] } = Maps.newDirectionFinder()
.setOrigin(origin)
.setDestination(destination)
.setMode(mode)
.getDirections();
if (!data) {
throw new Error('No route found!');
}
const { legs: [{ distance: { text: distance } } = {}] = [] } = data;
return distance;
};
/**
* Use Reverse Geocoding to get the address of
* a point location (latitude, longitude) on Google Maps.
*
* =GOOGLEMAPS_REVERSEGEOCODE(latitude, longitude)
*
* @param {String} latitude The latitude to lookup.
* @param {String} longitude The longitude to lookup.
* @return {String} The postal address of the point.
* @customFunction
*/
const GOOGLEMAPS_REVERSEGEOCODE = (latitude, longitude) => {
const { results: [data = {}] = [] } = Maps.newGeocoder().reverseGeocode(latitude, longitude);
return data.formatted_address;
};
/**
* Get the latitude and longitude of any
* address on Google Maps.
*
* =GOOGLEMAPS_LATLONG("10 Hanover Square, NY")
*
* @param {String} address The address to lookup.
* @return {String} The latitude and longitude of the address.
* @customFunction
*/
const GOOGLEMAPS_LATLONG = (address) => {
const { results: [data = null] = [] } = Maps.newGeocoder().geocode(address);
if (data === null) {
throw new Error('Address not found!');
}
const { geometry: { location: { lat, lng } } = {} } = data;
return `${lat}, ${lng}`;
};
/**
* Find the driving direction between two
* locations on Google Maps.
*
* =GOOGLEMAPS_DIRECTIONS("NY 10005", "Hoboken NJ", "walking")
*
* @param {String} origin The address of starting point
* @param {String} destination The address of destination
* @param {String} mode The mode of travel (driving, walking, bicycling or transit)
* @return {String} The driving direction
* @customFunction
*/
const GOOGLEMAPS_DIRECTIONS = (origin, destination, mode = 'driving') => {
const { routes = [] } = Maps.newDirectionFinder()
.setOrigin(origin)
.setDestination(destination)
.setMode(mode)
.getDirections();
if (!routes.length) {
throw new Error('No route found!');
}
return routes
.map(({ legs }) => {
return legs.map(({ steps }) => {
return steps.map((step) => {
return step.html_instructions.replace(/<[^>]+>/g, '');
});
});
})
.join(', ');
};
/**
* Calculate the travel time between two locations
* on Google Maps.
*
* =GOOGLEMAPS_DURATION("NY 10005", "Hoboken NJ", "walking")
*
* @param {String} origin The address of starting point
* @param {String} destination The address of destination
* @param {String} mode The mode of travel (driving, walking, bicycling or transit)
* @return {String} The time in minutes
* @customFunction
*/
/*
const GOOGLEMAPS_DURATION = (origin, destination, mode = 'driving') => {
const { routes: [data] = [] } = Maps.newDirectionFinder()
.setOrigin(origin)
.setDestination(destination)
.setMode(mode)
.getDirections();
if (!data) {
throw new Error('No route found!');
}
const { legs: [{ duration: { text: time } } = {}] = [] } = data;
return time;
};
*/
// The cache key for "New York" and "new york " should be same
const md5 = (key = '') => {
const code = key.toLowerCase().replace(/\s/g, '');
return Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, key)
.map((char) => (char + 256).toString(16).slice(-2))
.join('');
};
const getCache = (key) => {
return CacheService.getDocumentCache().get(md5(key));
};
// Store the results for 6 hours
const setCache = (key, value) => {
const expirationInSeconds = 6 * 60 * 60;
CacheService.getDocumentCache().put(md5(key), value, expirationInSeconds);
};
/**
* Calculate the travel time between two locations
* on Google Maps.
*
* =GOOGLEMAPS_DURATION("NY 10005", "Hoboken NJ", "walking")
*
* @param {String} origin The address of starting point
* @param {String} destination The address of destination
* @param {String} mode The mode of travel (driving, walking, bicycling or transit)
* @return {String} The time in minutes
* @customFunction
*/
const GOOGLEMAPS_DURATION = (origin, destination, mode = 'driving') => {
const key = ['duration', origin, destination, mode].join(',');
// Is result in the internal cache?
const value = getCache(key);
// If yes, serve the cached result
if (value !== null) return value;
const { routes: [data] = [] } = Maps.newDirectionFinder()
.setOrigin(origin)
.setDestination(destination)
.setMode(mode)
.getDirections();
if (!data) {
throw new Error('No route found!');
}
const { legs: [{ duration: { text: time } } = {}] = [] } = data;
// Store the result in internal cache for future
setCache(key, time);
return time;
};
/**
* Get the altitude of a location using the Google Maps Elevation API.
*
* =GOOGLEMAPS_ALTITUDE("Mount Everest")
*
* @param {string} location The address or coordinates of the location.
* @return {number|string} The altitude in meters, or an error message.
* @customFunction
*/
/**
* Get the altitude of a location using the Google Maps Elevation API (rounded to meters).
*
* =GOOGLEMAPS_ALTITUDE("Mount Everest")
*
* @param {string} location The address or coordinates of the location.
* @return {number|string} The altitude in meters (rounded), or an error message.
* @customFunction
*/
const GOOGLEMAPS_ALTITUDE = (location) => {
try {
const geocoder = Maps.newGeocoder();
const geocodeResult = geocoder.geocode(location);
if (!geocodeResult || !geocodeResult.results || geocodeResult.results.length === 0) {
return "Location not found.";
}
const { location: { lat, lng } } = geocodeResult.results[0].geometry;
const elevationResult = Maps.newElevationSampler().sampleLocation(lat, lng);
if (!elevationResult || !elevationResult.results || elevationResult.results.length === 0) {
return "Elevation data not available.";
}
return Math.round(elevationResult.results[0].elevation) + " m"; // Round to the nearest integer
} catch (e) {
return "Error: " + e.toString();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment