Using the Google Maps Javascript API

I’ve recently been playing around with the Google Maps Javascript API in my Rails web  apps to make some simple maps. Google’s documentation on this is very very good, so in most cases I’d recommend you start there to figure out how to make the map you want. Nonetheless, I thought I’d write up a quick tutorial for making a very simple map, making/displaying markers, and automatically re-positioning the map based on multiple location markers (from the perspective of using it in a Rails app).

I’ll start with Google’s Simple Map example.

  <div id="map"></div>
 <script>
 var map;
 function initMap() {
 map = new google.maps.Map(document.getElementById('map'), {
 center: {lat: -34.397, lng: 150.644},
 zoom: 8
 });
 }
 </script>
 <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
 async defer></script>

Remember to set the styling for #map to be whatever size you want the div to be in your stylesheet, because this is what will determine the size of your map.

The first obvious place to make this map dynamic is the lat, lng. Presumably you will have a variable declared in the controller that has a latitude and longitude associated with it, or perhaps you even have latitude and longitude variables available. In my case, I have @contact defined in the show action of my contacts controller as an instance of the contact class. Each contact instance has a latitude and longitude associated with it, based on the address input by the user (using Geocoder to look them up); from the create action:

city = Geocoder.search(params[:address])
contact.latitude = city[0].latitude
contact.longitude = city[0].longitude

So on the show view, the latitude and longitude would be accessible via @contact.latitude and @contact.longitude (respectively). And be sure to define your Google API key in the environmental variable hash, so you don’t go pushing that key up anywhere you wouldn’t want it to go. Actually, better go do that first.

Now you can put in your environmental variable for your API key, as well as embedded Ruby for your latitude and longitude:

<script>
      var map;
      function initMap() {
        var myLatLng = {lat: <%= @contact.latitude %>, lng: <%= @contact.longitude %>};

        map = new google.maps.Map(document.getElementById('map'), {
          center: myLatLng,
          zoom: 12
        });
      }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV["google_key"] %>&callback=initMap"
    async defer></script>

That will make a simple map centered on your latitude and longitude. But obviously, what we all really want is a marker. So, let us put in a marker!

<script>
  var map;
  function initMap() {
    var myLatLng = {lat: <%= @contact.latitude %>, lng: <%= @contact.longitude %>};

    map = new google.maps.Map(document.getElementById('map'), {
      center: myLatLng,
      zoom: 12
    });

    // makes a marker
    var marker = new google.maps.Marker({
      position: myLatLng, //at your latitude and longitude
      map: map, //on the map called map
    });
  }
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV["google_key"] %>&callback=initMap"
async defer></script>

Feel free to play around with the zoom until you get it to where you like it. I find that 12 is pretty good for a single marker, but your tastes/needs may vary. The higher the number, the higher the zoom. So zoom: 1 will basically give you a map of the whole world. Which, maybe you want!

But now, what if you have a bunch of markers, and you want to adjust the center/zoom based on them? Obviously you wouldn’t be able to just use one marker’s latitude and longitude to determine the center anymore. Well, there is a simple way to do this, too! All you need to do is define a LatLngBounds variable, and then use the extend and fitBounds methods. For this example, I put the map in the contacts index, so there would be multiple lat, long coordinates to play with:

<script>
   var map;
   function initMap() {
     var contacts = <%= raw @script_contacts %>;
 
     var bounds = new google.maps.LatLngBounds(); //these will define the bounds of your map
     map = new google.maps.Map(document.getElementById('map'), {}); //this makes your map caled map

     for (var i = 0; i < contacts.length; i++) { //Iterate over all the contacts
       var contact = JSON.parse(contacts[i]);
       var marker = new google.maps.Marker({ //Make a marker for each contact as you iterate
         position: {lat: contact.latitude, lng: contact.longitude},
         map: map,
       });
       bounds.extend(marker.position); //will extend your bounds each time a contact is added
     } 
 
     //refit the map to the bounds as defined in your each loop
     map.fitBounds(bounds);
    }
 </script>

Et voila! It really is that easy. And now you have a map that will re-center/re-zoom to fit/show all the markers on it. If you want to force the zoom to a certain level, and only let the centering change, all you have to do is define the zoom anywhere after you use the fitBounds method on your map. I don’t really like forcing the zoom, though, I find I like it best when the map finds it own zoom.

Oh, yea, and this is what happens when you use the Faker gem to randomly generate a bunch of latitudes and longitudes for a bunch of randomly generated contacts… you get contacts in the middle of the ocean, lol:

map of randomly generated locations
You’d be surprised at how many people live in the middle of the ocean these days

But I got sick of manually entering actual addresses, so now I have ocean contacts. This is the generally-accepted tradeoff.

Medieval FEAST!

Doc knows how obsessed I am with all things medieval, and he was so kind as to buy some tickets to a Medieval Feast event! I got so excited, I made a double hennin and everything.

Going Medieval
Me at the Medieval Feast as photographed by Doc

This got me super obsessed with Medieval food, and soon I stumbled upon the Boke of Gode Cookery, and in particular the recipe for Sambocade (elderflower cheesecake). So of course, I had to make it.

But, like, of course I had to first make cheese curds! Because the recipe explicitly says the first step is the strain the whey from the curds.

Cutting curds with saffron in them
Cutting the curds. And as you can see, I decided to stir some saffron threads into the milk before adding the rennet and letting it set. For a moar medieval flavor!!!

Ladling saffron curds into cheese cloth to strain out whey
“Wryng out þe wheyze and drawe hem þurgh a straynour”

Straining out whey from curds
The saffron curds actually tasted really, really good. I’m now thinking that a saffron panna cotta would be amazing.

Medieval elderflower cheesecake!
Tah-daaaah! Medieval cheesecake!

I would recommend serving it with some caudell drizzled on top.

Note that, I didn’t care about improving the recipe or adding new-fangled things to better suit a modern palate. All I cared about was that I would eat a thing that was literally what a medieval person ate at least once. Because that’s who I am.

This led me into some trouble trying to interpret the recipe. The recipe appears to say to strain the whey from the curds and put them into the pie shell. THEN, add the egg whites and elderflowers and rose water. But the “modern translation” says to mix everything together until smooth, and pour the whole thing into the pie shell. So, which is it? Is this dish more like a cheesey crust with a meringue on top? Or is it more like a modern version of a cheesecake, with one layer of a smooth cheesey homogeneous mix?

Ultimately, I decided to go with what the “modern translation” said, figuring that medieval authors generally didn’t have the same level of explicit instruction that we do today, and that maybe the translators know better than I do what the intent was. But sometimes I wonder!

A Tale of Two Laptops

So I’ve finally decided to get a Mac. “But Steen!” my friends say, “I thought you were Mrs. All-Linux-All-The-Time!”

Well, I still am! Even though I still like others better, to be honest Macs also essentially run on a Linux distro.

Initial thoughts:

  • It looks like I will have to manually install Basemap to get it working on the Mac so I can do this on it, as even Homebrew doesn’t have it. Which isn’t so bad, but since I already have a working machine with Basemap set up, it is easy for me to slide into laziness and not manually install it on the Mac. But I’ll probably do it eventually.
  • Macs have `say`
  • MACS ARE MADE OF METAL. Nothing drives home the reality of how often you use your laptop without pants quite like getting a metal laptop.
  • I only use the terminal on my Mac because I cannot figure out the GUI for the life of me. Like, what the heck is going on there? At least the terminal is familiar!

But then again, there’s this:

That is legitimately terrifying :/

I’ll be sure to continue using my other machine for any sort of bioinformatic work.

But it is not all bad, I always have fun getting to know a new computer. I’m looking forward to seeing how this all pans out.