diff --git a/index.html b/index.html new file mode 100644 index 0000000..a806f50 --- /dev/null +++ b/index.html @@ -0,0 +1,44 @@ + + + + + Citi Bike + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..56acbe4 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,7 @@ +html, +body, +#map-id { + height: 100%; + padding: 0; + margin: 0; +} diff --git a/static/js/leaflet.extra-markers.min.js b/static/js/leaflet.extra-markers.min.js new file mode 100644 index 0000000..9ae8973 --- /dev/null +++ b/static/js/leaflet.extra-markers.min.js @@ -0,0 +1 @@ +!function(a,b){"use strict";L.ExtraMarkers={},L.ExtraMarkers.version="1.0.1",L.ExtraMarkers.Icon=L.Icon.extend({options:{iconSize:[35,45],iconAnchor:[17,42],popupAnchor:[1,-32],shadowAnchor:[10,12],shadowSize:[36,16],className:"extra-marker",prefix:"",extraClasses:"",shape:"circle",icon:"",innerHTML:"",markerColor:"red",iconColor:"#fff",number:""},initialize:function(a){a=L.Util.setOptions(this,a)},createIcon:function(){var a=b.createElement("div"),c=this.options;return c.icon&&(a.innerHTML=this._createInner()),c.innerHTML&&(a.innerHTML=c.innerHTML),c.bgPos&&(a.style.backgroundPosition=-c.bgPos.x+"px "+-c.bgPos.y+"px"),this._setIconStyles(a,c.shape+"-"+c.markerColor),a},_createInner:function(){var a="",b="",c=this.options;return c.iconColor&&(a="style='color: "+c.iconColor+"' "),c.number&&(b="number='"+c.number+"' "),""},_setIconStyles:function(a,b){var c,d,e=this.options,f=L.point(e["shadow"===b?"shadowSize":"iconSize"]);"shadow"===b?(c=L.point(e.shadowAnchor||e.iconAnchor),d="shadow"):(c=L.point(e.iconAnchor),d="icon"),!c&&f&&(c=f.divideBy(2,!0)),a.className="leaflet-marker-"+d+" extra-marker-"+b+" "+e.className,c&&(a.style.marginLeft=-c.x+"px",a.style.marginTop=-c.y+"px"),f&&(a.style.width=f.x+"px",a.style.height=f.y+"px")},createShadow:function(){var a=b.createElement("div");return this._setIconStyles(a,"shadow"),a}}),L.ExtraMarkers.icon=function(a){return new L.ExtraMarkers.Icon(a)}}(window,document); \ No newline at end of file diff --git a/static/js/logic (1).js b/static/js/logic (1).js new file mode 100644 index 0000000..d617494 --- /dev/null +++ b/static/js/logic (1).js @@ -0,0 +1,63 @@ +// var newYorkCoords = [40.73, -74.0059]; +// var mapZoomLevel = 12; + +// Create the createMap function. +function createMap(bikeStations) { + + + // Create the tile layer that will be the background of our map. + let streetmap = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }); + + // Create a baseMaps object to hold the lightmap layer. + let baseMaps = { + "Street Map": streetmap + }; + + // Create an overlayMaps object to hold the bikeStations layer. + let overlayMaps = { + "Bike Stations": bikeStations + }; + + // Create the map object with options. + let map = L.map("map-id", { + center: [40.73, -74.0059], + zoom: 12, + layers: [streetmap, bikeStations] + }); + + // Create a layer control, and pass it baseMaps and overlayMaps. Add the layer control to the map. + L.control.layers(baseMaps, overlayMaps, { + collapsed: false + }).addTo(map); + +} +// Create the createMarkers function. +function createMarkers(response) { + + // Pull the "stations" property from response.data. + let stations = response.data.stations; + + // Initialize an array to hold the bike markers. + let bikeMarkers =[]; + + // Loop through the stations array. + // For each station, create a marker, and bind a popup with the station's name. + for (let i = 0; i < stations.length; index++) { + let station = stations[index]; + + let bikeMarker = L.marker([station.lat, station.lon]) + .bindPopup("

" + station.name + "

Capacity: " + station.capacity + "

"); + + + // Add the marker to the bikeMarkers array. + bikeMarkers.push(bikeMarkers); + } + // Create a layer group that's made from the bike markers array, and pass it to the createMap function. + createMap(L.layerGroup(bikeMarkers)); +} +let bikeurl = "https://gbfs.citibikenyc.com/gbfs/en/station_information.json"; + +// Perform an API call to the Citi Bike API to get the station information. Call createMarkers when it completes. +d3.json(bikeurl).then(createMarkers); diff --git a/static/js/logic.js b/static/js/logic.js new file mode 100644 index 0000000..790b5e3 --- /dev/null +++ b/static/js/logic.js @@ -0,0 +1,169 @@ +// let newYorkCoords = [40.73, -74.0059]; +// let mapZoomLevel = 12; + +// Create the tile layer that will be the background of our map. +let streetmap = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' +}); + +// Initialize all the LayerGroups that we'll use. +let layers = { + COMING_SOON: new L.LayerGroup(), + EMPTY: new L.LayerGroup(), + LOW: new L.LayerGroup(), + NORMAL: new L.LayerGroup(), + OUT_OF_ORDER: new L.LayerGroup() +}; + +// Create the map with our layers. +let map = L.map("map-id", { + center: [40.73, -74.0059], + zoom: 12, + layers: [ + layers.COMING_SOON, + layers.EMPTY, + layers.LOW, + layers.NORMAL, + layers.OUT_OF_ORDER + ] +}); + +// Add our "streetmap" tile layer to the map. +streetmap.addTo(map); + +// Create an overlays object to add to the layer control. +let overlays = { + "Coming Soon": layers.COMING_SOON, + "Empty Stations": layers.EMPTY, + "Low Stations": layers.LOW, + "Healthy Stations": layers.NORMAL, + "Out of Order": layers.OUT_OF_ORDER +}; + +// Create a control for our layers, and add our overlays to it. +L.control.layers(null, overlays).addTo(map); + +// Create a legend to display information about our map. +let info = L.control({ + position: "bottomright" +}); + +// When the layer control is added, insert a div with the class of "legend". +info.onAdd = function() { + var div = L.DomUtil.create("div", "legend"); + return div; +}; +// Add the info legend to the map. +info.addTo(map); + +// Initialize an object that contains icons for each layer group. +let icons = { + COMING_SOON: L.ExtraMarkers.icon({ + icon: "ion-settings", + iconColor: "white", + markerColor: "yellow", + shape: "star" + }), + EMPTY: L.ExtraMarkers.icon({ + icon: "ion-android-bicycle", + iconColor: "white", + markerColor: "red", + shape: "circle" + }), + OUT_OF_ORDER: L.ExtraMarkers.icon({ + icon: "ion-minus-circled", + iconColor: "white", + markerColor: "blue-dark", + shape: "penta" + }), + LOW: L.ExtraMarkers.icon({ + icon: "ion-android-bicycle", + iconColor: "white", + markerColor: "orange", + shape: "circle" + }), + NORMAL: L.ExtraMarkers.icon({ + icon: "ion-android-bicycle", + iconColor: "white", + markerColor: "green", + shape: "circle" + }) +}; + +// Perform an API call to the Citi Bike station information endpoint. +d3.json("https://gbfs.citibikenyc.com/gbfs/en/station_information.json").then(function(infoRes) { + + // When the first API call completes, perform another call to the Citi Bike station status endpoint. + d3.json("https://gbfs.citibikenyc.com/gbfs/en/station_status.json").then(function(statusRes) { + let updatedAt = infoRes.last_updated; + let stationStatus = statusRes.data.stations; + let stationInfo = infoRes.data.stations; + + // Create an object to keep the number of markers in each layer. + let stationCount = { + COMING_SOON: 0, + EMPTY: 0, + LOW: 0, + NORMAL: 0, + OUT_OF_ORDER: 0 + }; + + // Initialize stationStatusCode, which will be used as a key to access the appropriate layers, icons, and station count for the layer group. + let stationStatusCode; + + // Loop through the stations (they're the same size and have partially matching data). + for (let i = 0; i < stationInfo.length; i++) { + + // Create a new station object with properties of both station objects. + let station = Object.assign({}, stationInfo[i], stationStatus[i]); + // If a station is listed but not installed, it's coming soon. + if (!station.is_installed) { + stationStatusCode = "COMING_SOON"; + } + // If a station has no available bikes, it's empty. + else if (!station.num_bikes_available) { + stationStatusCode = "EMPTY"; + } + // If a station is installed but isn't renting, it's out of order. + else if (station.is_installed && !station.is_renting) { + stationStatusCode = "OUT_OF_ORDER"; + } + // If a station has less than five bikes, it's status is low. + else if (station.num_bikes_available < 5) { + stationStatusCode = "LOW"; + } + // Otherwise, the station is normal. + else { + stationStatusCode = "NORMAL"; + } + + // Update the station count. + stationCount[stationStatusCode]++; + // Create a new marker with the appropriate icon and coordinates. + let newMarker = L.marker([station.lat, station.lon], { + icon: icons[stationStatusCode] + }); + + // Add the new marker to the appropriate layer. + newMarker.addTo(layers[stationStatusCode]); + + // Bind a popup to the marker that will display on being clicked. This will be rendered as HTML. + newMarker.bindPopup(station.name + "
Capacity: " + station.capacity + "
" + station.num_bikes_available + " Bikes Available"); + } + + // Call the updateLegend function, which will update the legend! + updateLegend(updatedAt, stationCount); + }); +}); + +// Update the legend's innerHTML with the last updated time and station count. +function updateLegend(time, stationCount) { + document.querySelector(".legend").innerHTML = [ + "

Updated: " + moment.unix(time).format("h:mm:ss A") + "

", + "

Out of Order Stations: " + stationCount.OUT_OF_ORDER + "

", + "

Stations Coming Soon: " + stationCount.COMING_SOON + "

", + "

Empty Stations: " + stationCount.EMPTY + "

", + "

Low Stations: " + stationCount.LOW + "

", + "

Healthy Stations: " + stationCount.NORMAL + "

" + ].join(""); +} \ No newline at end of file diff --git a/static/js/style.js b/static/js/style.js new file mode 100644 index 0000000..8897f73 --- /dev/null +++ b/static/js/style.js @@ -0,0 +1,25 @@ +// Toggle Animation by Class + +;(function($) { + $(function() { + $('nav ul li > a:not(:only-child)').click(function(e) { + $(this) + .siblings('.nav-dropdown') + .slideToggle() + $('.nav-dropdown') + .not($(this).siblings()) + .hide() + e.stopPropagation() + }) + $('html').click(function() { + $('.nav-dropdown').hide() + }) + // Toggle open and close nav styles on click + $('#nav-toggle').click(function() { + $('nav ul').slideToggle(); + }); + $('#nav-toggle').on('click', function() { + this.classList.toggle('active') + }) + }) +})(jQuery) \ No newline at end of file