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:
But I got sick of manually entering actual addresses, so now I have ocean contacts. This is the generally-accepted tradeoff.
Nice, really informative.
I can’t get the map for the index working. What is @script_contacts defined as?
Ah-hah, right! I didn’t show the contacts controller here. Basically, @script_contacts is an array of all the contacts, converted to JSON to make it compatible with Javascript (because Javascript doesn’t play nice with the ActiveRecord instances 🙂
I feel like the way I worked around this was clunky, but in the contacts controller, I have:
contacts = Contact.all
@script_contacts = []
contacts.each do |contact|
@script_contacts << contact.to_json end
Annnd I don't know how to make the formatting better in the comments, but you get the idea.
Text (512 bytes): {. “locations” : [ {. “timestampMs” : “1495377892988”,. “latitudeE7” : 413893745,. “longitudeE7” : -850595513,. “accuracy” : 1500,. “activity” : [ {. “timestampMs” : “1495378150662”,. “activity” : [ {. “type” : “EXITING_VEHICLE”,. “confidence” : 100. } ]. }, {. “timestampMs” : “1495378150660”,. “activity” : [ {. “type” : “ON_FOOT”,. “confidence” : 39. }, {. “type” : “ON_BICYCLE”,. “confidence” : 23. }, {. …
Can anyone please help me determine where this location is ???
Hey Scott,
That location from a Google Location History JSON file is at 41.3893745, -85.0595513 and it appears to be in Auburn, Indiana.
The Google Location History GeoJSON files report latitude and longitudes in E7, so to get the more common lat/long coordinates, you just have to divide those numbers by 10,000,000.
Go to this URL to see it in Google Maps:
https://www.google.com/maps/place/41%C2%B023'21.8%22N+85%C2%B003'34.4%22W/@41.3893785,-85.0770608,14z
Hope that helps!