Where am I? In real time: whereis.jshowers.com
When I went full time digital nomad, I started using a public Google calendar to share with family and friends where I was. This also helped me organize my thoughts and travel dates.
I wanted to make a simple graphical representation of this calendar, so I decided to make a site that would use the data I already have in the calendar. I wanted to show where I currently am, where I was previously, and where I’m going next. I also wanted to show a google map with a pin at my current location.
Disclaimer: there are a thousand different ways I could have done this, and a thousand more ideas to expand on it. This works, and is very basic.
HTML
I used bootstrap for page layout, as I can whip it together easily. The page consists of a basic page-header class and a 3-column table layout.
<div class="page-header text-center"> <h1>Where is Jasper?</h1> </div> <table class="table text-center"> <thead> <tr> <th class="text-center"><h3><span class="label label-default">Previous</span></h3> <th class="text-center"><h3><span class="label label-primary">Now</span></h3></th> <th class="text-center"><h3><span class="label label-default">Next</span></h3></th> </tr> </thead> <tbody> <tr> <td><h3 id="past-place"></h3></td> <td><h3 id="current-place"></h3></td> <td><h3 id="next-place"></h3></td> </tr> </tbody> </table> <div id="map"></div>
JavaScript Libraries
To make this work I need:
- The current date, in order to see which calendar event (location) we are currently on.
- MomentJS is a quick and easy date parsing and comparison library.
- List of all calendar events.
- The Google Calendar API makes this easy with a public calendar ID.
- Figure out which event we are currently on by comparing today’s date with event dates.
- Basic date comparison logic.
- Figure out the latitude/longitude of the place we are in (from the calendar event’s title).
- Using Google Places API, assuming the first hit is the correct one.
- Show a map and center it on the current location with a dropped pin.
- This uses the Google Maps JavaScript API.
Other libraries used:
- jQuery – oh yeah baby, like it’s 2008! This is a dependency of Bootstrap and also makes it easy to add our place names to the HTML elements. Also an easy way to make our API calls.
JavaScript Code
Now the fun stuff!
First some constants and a date comparison function for sorting calendar events.
const date_now = moment().format("YYYY-MM-DD"); const calendar_id = "{Calendar ID}"; const api_key = "{Google API Key}"; function compare_dates(a, b) { if ( a.end.date < b.end.date ){ return -1; } if ( a.end.date > b.end.date ){ return 1; } return 0; }
Now we get the list of all calendar events, sort them by date, and get our current, past, and future events. We also put the calendar invite name (the location, like “London, England”) into our HTML for display.
$.get({ url:`https://www.googleapis.com/calendar/v3/calendars/${calendar_id}/events?key=${api_key}`, success: function(data) { let events = data.items; // Sort list since &sortBy doesn't seem to work with the google calendar API call and this event type events.sort(compare_dates) // Find today's event let current_event_index = events.findIndex( event => event.end.date >= date_now ); let current_event = events[current_event_index] // Since our events are sorted, // the previous and next events will be +/- one index away let past_event = events[current_event_index - 1] let next_event = events[current_event_index + 1] // Insert the event summary (the place name in my calendar) into our HTML $("#past-place").text(past_event.summary); $("#current-place").text(current_event.summary); $("#next-place").text(next_event.summary); // Now iniit the Google maps with our current place selected initMap(current_event.summary) } });
Finally, we initialize the map on the page, passing in our current location. This searches the Places API to get the corresponding latitude/longitude in order to center the map at that location and to drop a pin there.
// Initialize and add the map let map; let map_element = document.getElementById("map") async function initMap(current_location) { // Request needed libraries const { Map } = await google.maps.importLibrary("maps"); const { AdvancedMarkerView } = await google.maps.importLibrary("marker"); const { Places } = await google.maps.importLibrary("places"); // Construct the Map object map = new Map(map_element, { zoom: 5, }); // Places request to get lat/long based on passed in location var request = { query: current_location, fields: ['name', 'geometry'], }; var service = new google.maps.places.PlacesService(map); service.findPlaceFromQuery(request, function(results, status) { if (status === google.maps.places.PlacesServiceStatus.OK) { // Assume top result is fine, place marker on map let top_result = results[0] const marker = new AdvancedMarkerView({ map: map, position: top_result.geometry.location, title: top_result.name, }); map.setCenter(top_result.geometry.location); } }); }
Tada! Mission accomplished.