Display Kintone Records on Google Maps
Originally written for the Kintone Customization Contest on dev.to
TL;DR
- Sample code that displays locations on Google Maps from a Kintone App
- Each record has an address field
- The address field is geocoded to a latitude and longitude using Google Maps API
- The latitude and longitude is displayed on Google Maps
- Everything is stored in my GitHub repo: emilythecat/googlemaps-kintone
Demo
Introduction
Here is a blog post and sample code to display Kintone records on Google Maps. It is a submission for the Kintone Customization Contest 2023.
As an example, I have created a "UC Campus" app that lists the University of California campuses. Each record has an address field.
Prerequisites
- Kintone Developer License - Form
- Google Maps API Key - Video tutorial
Steps
-
Create a Kintone App with the following fields:
Field Name Field Code Field Type UC Campus name
Text Founded founded
Number Enrollment enrollment
Number Endowment endowment
Text Mascots mascots
Text Address address
Text - map
Space -
Import the data from the
uc-campus.csv
file (provided below) -
Copy the
google-maps-kintone.js
file to your local machine (provided below) -
Insert your Google Maps API Key into the
google-maps-kintone.js
file:const API_KEY = 'Insert_your_API_key_here';
-
Upload the
google-maps-kintone.js
file to the Kintone App's JavaScript and CSS Customization settings -
All done! Refresh the page to universities the Google Maps
Screenshots of the Steps
Debugging
- API key is invalid - verify that the API key is correct, has Google Maps API enabled, and Kintone's domain is allowed
- Verify that the
address
field is filled out for each record - Verify that the Kintone's field codes are matching in the values in the
google-maps-kintone.js
file
Files
uc-campus.csv
UC Campus,Founded,Enrollment (2022),Endowment (2022),Mascots, Address
UC Berkeley,1868,"45307",$6.91 billion,Golden Bears, "University Avenue and, Oxford St, Berkeley, CA 94720, USA"
UC Davis,1905,"39679",$2.06 billion,Aggies, "1 Shields Ave, Davis, CA 95616, USA"
UC Irvine,1965,"35937",$1.25 billion,Anteaters, "260 Aldrich Hall
Irvine, CA 92697, USA"
UC Los Angeles,1919,"46430",$6.72 billion,Bruins, "Bunche Hall, 315 Portola Plaza, Los Angeles, CA 90095, USA"
UC Merced,2005,"9103",$85 million,Golden Bobcats, "5200 Lake Rd, Merced, CA 95343, USA"
UC Riverside,1954,"26809",$354 million,Highlanders, "900 University Ave, Riverside, CA 92521, USA"
UC San Diego,1960,"42006",$2.39 billion,Tritons, "9500 Gilman Dr, La Jolla, CA 92093, USA"
UC San Francisco,1864,"3140",$5.46 billion,Bears, "505 Parnassus Ave, San Francisco, CA 94143, USA"
UC Santa Barbara,1909,"26420",$544 million,Gauchos, "Bldg, Davidson Library, 525 UCEN Rd, Isla Vista, CA 93106, USA"
UC Santa Cruz,1965,"19478",$269 million,Banana Slugs, "1156 High St, Santa Cruz, CA 95064, USA"
google-maps-kintone.js
(() => {
'use strict';
// Google Maps Settings
const API_KEY = 'Insert_your_API_key_here';
const GOOGLE_MAPS_URL = 'https://maps.googleapis.com/maps/api/js?v=3';
const GOOGLE_GEOCODE_URL = 'https://maps.googleapis.com/maps/api/geocode/json';
const CENTER_POINT = { lat: 37.16611, lng: -119.44944 };
const MAP_LANGUAGE = 'en';
const MAP_COUNTRY = 'US';
const APP_MAP_ZOOM = 6;
const RECORD_MAP_ZOOM = 15;
// Kintone Field Codes
const TITLE_FIELD_CODE = 'name';
const ADDRESS_FIELD_CODE = 'address';
const SPACE_FIELD_CODE = 'map';
// Function to generate a link to a record's details page
const getRecordLink = recordID => {
const hostname = window.location.hostname;
const appID = kintone.app.getId();
return `<a href="https://${hostname}/k/${appID}/show#record=${recordID}">More Info</a>`;
};
// Function to retrieve coordinates for given addresses using Google Geocoding API
const getAddressCoordinates = async addresses => {
const coordinates = [];
for (const [id, name, address] of addresses) {
const response = await fetch(`${GOOGLE_GEOCODE_URL}?address=${encodeURIComponent(address)}&key=${API_KEY}`);
const data = await response.json();
if (data.results && data.results[0]?.geometry?.location) {
const { lat, lng } = data.results[0].geometry.location;
coordinates.push([id, name, lat, lng]);
}
}
return coordinates;
};
// Function to load an external script dynamically
const loadScript = src => {
const head = document.head || document.getElementsByTagName('head')[0];
const script = document.createElement('script');
script.src = src;
head.appendChild(script);
};
// Check if Google Maps API is loaded
const isGoogleMapsLoaded = () => (typeof google !== 'undefined') && (typeof google.maps !== 'undefined');
// Load Google Maps API if not already loaded
const loadGoogleMaps = () => {
if (!isGoogleMapsLoaded()) {
loadScript(`${GOOGLE_MAPS_URL}&key=${API_KEY}&callback=initMap`);
}
};
// Display a map on the record details page
kintone.events.on('app.record.detail.show', (event) => {
// Function to draw a map based on address information
const drawMap = () => {
if (kintone.app.record.getFieldElement(ADDRESS_FIELD_CODE).length === 0) {
console.log('Enter an address in the "Address" field');
return;
}
if (document.getElementsByName('mapOutput').length !== 0) {
return;
}
// Create a map element
const mapAddressEl = document.createElement('div');
mapAddressEl.id = mapAddressEl.name = 'mapOutput';
kintone.app.record.getSpaceElement(SPACE_FIELD_CODE).appendChild(mapAddressEl);
// Geocode the address and display the map
const gc = new google.maps.Geocoder();
const addressValue = kintone.app.record.get().record[ADDRESS_FIELD_CODE].value;
gc.geocode({
address: addressValue,
language: MAP_LANGUAGE,
country: MAP_COUNTRY
}, (results, status) => {
if (status === google.maps.GeocoderStatus.OK) {
mapAddressEl.style.cssText = 'width: 300px; height: 250px';
const point = results[0].geometry.location;
const opts = {
zoom: RECORD_MAP_ZOOM,
center: point,
mapTypeId: google.maps.MapTypeId.ROADMAP,
scaleControl: true
};
const map = new google.maps.Map(mapAddressEl, opts);
new google.maps.Marker({
position: point,
map,
title: results[0].formatted_address
});
}
});
};
// Load Google Maps and draw map on details page
if (!document.getElementsByName('map_latlng').length) {
loadGoogleMaps();
let timeout = 10000; // 10 seconds
const interval = 100; // 100ms
const checkGoogleMaps = setInterval(() => {
if (isGoogleMapsLoaded()) {
drawMap();
clearInterval(checkGoogleMaps);
} else if ((timeout -= interval) <= 0) {
clearInterval(checkGoogleMaps);
}
}, interval);
}
});
// Display a map on the record list page
kintone.events.on('app.record.index.show', async event => {
// Create a map element
const spaceDiv = kintone.app.getHeaderSpaceElement();
spaceDiv.style.cssText = 'height: 500px; margin-left: 25px; margin-right: 25px; border: solid; border-color: #bf7bed;';
spaceDiv.id = 'map';
// Prepare address data for geocoding
const addressData = event.records.map(record => {
return [record.$id.value, `${record[TITLE_FIELD_CODE].value}<br>${record[ADDRESS_FIELD_CODE].value}<br>${getRecordLink(record.$id.value)}`, record[ADDRESS_FIELD_CODE].value];
});
try {
// Get coordinates and initialize map on list page
const coordinates = await getAddressCoordinates(addressData);
window.initMap = () => {
const map = new google.maps.Map(spaceDiv, {
zoom: APP_MAP_ZOOM,
center: CENTER_POINT,
mapTypeId: "terrain",
});
const infoWindow = new google.maps.InfoWindow({});
coordinates.forEach(([id, name, lat, lng], index) => {
const marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map,
title: name
});
marker.addListener('click', () => {
infoWindow.setContent(name);
infoWindow.open(map, marker);
});
});
};
// Load Google Maps and set up map on list page
if (!document.getElementsByName('map_latlng').length) {
loadGoogleMaps();
let timeout = 10000; // 10 seconds
const interval = 100; // 100ms
const checkGoogleMaps = setInterval(() => {
if (isGoogleMapsLoaded()) {
clearInterval(checkGoogleMaps);
} else if ((timeout -= interval) <= 0) {
clearInterval(checkGoogleMaps);
}
}, interval);
}
} catch (error) {
console.error("Error:", error);
}
});
})();
This post is part of the Kintone Customization Contest 2023.
Submitter: https://forum.kintone.dev/u/emilythecat/