DEV Community

Cover image for Display Kintone Records on Google Maps
Emily The Cat
Emily The Cat

Posted on • Originally published at github.com

2

Display Kintone Records on Google Maps

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

List View Record View
img-demo-list.png img-demo-record.png

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

Steps

  1. 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
  2. Import the data from the uc-campus.csv file (provided below)

  3. Copy the google-maps-kintone.js file to your local machine (provided below)

  4. Insert your Google Maps API Key into the google-maps-kintone.js file:

    • const API_KEY = 'Insert_your_API_key_here';
  5. Upload the google-maps-kintone.js file to the Kintone App's JavaScript and CSS Customization settings

  6. All done! Refresh the page to universities the Google Maps

Screenshots of the Steps

Step 1 Step 2 Step 3
img-kintone-form.png img-kintone-import.png img-kintone-js.png

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"
Enter fullscreen mode Exit fullscreen mode

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);
    }
  });
})();
Enter fullscreen mode Exit fullscreen mode

This post is part of the Kintone Customization Contest 2023.
Submitter: https://forum.kintone.dev/u/emilythecat/

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (1)

Collapse
 
ahandsel profile image
ahandsel • Edited

Thank you for submitting your project to the Kintone Customization Contest 2023!

Congratulations on your win!
We will be reaching out to you through the Kintone Developer Forum.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay