Skip to content

Instantly share code, notes, and snippets.

@ashleygwilliams
Last active June 26, 2016 01:29
Show Gist options
  • Save ashleygwilliams/5877521 to your computer and use it in GitHub Desktop.
Save ashleygwilliams/5877521 to your computer and use it in GitHub Desktop.
an app to practice forms in sinatra, using JSON station data from citibike NYC.

#citibike citibike Sinatra forms

Objectives

  1. create a form in HTML
  2. using ERB, populate a select element with options from a JSON file
  3. understand how to use the name and value attributes to populate a params hash of user inputted data
  4. create a route to respond to a post from the form
  5. create instance variables from the params hash in your route so you can use it in a view
  6. use embedded ruby to plot points on a map using javascript

Instructions

Get up and running

  1. fork and then clone this repo: https://github.com/ashleygwilliams/citibike-sinatra
  2. bundle install
  3. bundle exec shotgun
  4. localhost:9393

Get familiar

This app is using the MultiJSON gem to load in data. If you look in app.rb you'll see that we are using something called a before filter to load the JSON into an instance variable called @data, which will hold our data from the JSON file.

#app.rb

  before do
    json = File.open("data/citibikenyc.json").read
    @data = MultiJson.load(json)
  end

The app has a single route, at "/" that will simple print out the contents of @data.

#app.rb

  get '/' do
    erb :home
  end
#home.erb

  <%= @data.inspect %>

Change the code in home.erb so that it prints a list of every station name, followed by it's longitude and latitude.

Make a form

  1. create a route that respond to a get request at /form
  2. create a view, and update the route def so that /form will render that view
  3. put a heading on the page with the text "Map my route!"
  4. inside your view, create a <form> element with 2 <select> elements, one with name="start" and the other with name="end". also create a submit button.
  5. label each <select> element with the words "start:" and "end:" respectively
  6. inside the <select> elements, loop through the data hash and create an <option> element for each station with the value of the station name

you should now have a form that looks like this: form

Make the form work

  1. your form should post to /form, so update your <form> element
  2. create a route in app.rb
  3. investigate your form's response by printing params to the browser when the form is submitted
  4. once you feel comfortable with your form's response, change the response to a string that read "You chose ______ and _______" where the blanks contain the name of the station that the user chose

MAP what?

the app you cloned also has a view called map.erb. this map uses leaflet.js. the code in the view looks like this:

<div id="map"></div>
<script type="text/javascript" charset="utf-8">
  var map = L.map('map').setView([40.7142, -74.0064], 13);
  L.tileLayer('http://tile.stamen.com/toner/{z}/{x}/{y}.png', {attribution: 'Stamen Toner'}, {
    attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://cloudmade.com">CloudMade</a>',
    maxZoom: 18
  }).addTo(map);
</script>

basically, this code makes a div with id=map. the <script> contains javascript that makes a map and puts it into that div.

  1. create a route at /map and have it render this view. you should see a map, with no points. it'll look like this: map

Map the data

Let's connect our map to the data we receive from the form now.

A. respond to the form post with the map view

  1. change the action on your form to the /map route.
  2. change your map route to respond to a post instead of a get
  3. test your app to ensure that everything still works

B. play with params again

  1. in your map route, create 2 instance variables and store the start and end station names in them
  2. in map.erb create an unordered list and print the name of the start and end station name to the screen

C. from names to locations

so we're getting name data, but we actually want longitude and latitude data. where is that happening and what should we do to change it?

  1. take a moment and look at your code. can you change the params that the form is sending to the map view? what are your options? play around for a second.

so, the params are determined by the value attribute of the <select> element. originally, we were just putting the station's name in there.. but we can change that.

  1. change ther value of each <select> element to an array, where the first element is the latitude and the second element is the longitude of any given station. test this by printing the params to the map page. you'll want to see something like this:

latlong

ooops. this data doesn't look right. we need to fix it. look at sarah's blog post for advice on how to do this: http://sarahduve.github.io/blog/2013/06/27/dont-forget-to-float/

D. embed the ruby in js

ok awesome, so now we have an array for the starting location and the ending location. the array contains the latitude and longitude for the selected station. this is rad because this is ALL we need to create markers and draw shapes and lines using the leaflet js mapping library.

you can add a marker to a leaflet map using the following code.

L.marker([longitude, latitude]).addTo(map);

here:

  • L is the leaflet library
  • .marker is a method that creates a marker object. it takes one parameter, an array with 2 elements, longitude and latitude
  • .addTo is a method that is called on an object and adds it to a map, which is passed as the parameter map (NOTE: that map was made earlier using this code var map = L.map('map').setView([40.7142, -74.0064], 13);)

so now:

  1. add a marker for the start and end locations

in addition to markers, leaflet also allows you to draw lines and shapes and all sorts of things.

making a line is really similar to making a marker; except you use .polyline instead of .marker and you just simply pass it more points as parameters:

L.polyline([
    [lat1, lng1],
    [lat2, lng2]
]).addTo(map);

using that knowledge 2. create a line from the start location to the end location.

if you want... take a look at the documentation. you can change how the line looks and even make your map zoom to fit your line! http://leafletjs.com/reference.html#polyline

you now have something that looks like this: complete

@micahrcorn
Copy link

Ashley, what repo do we fork and clone to do this assignment?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment