├── CSS ├── leaflet.label.css └── styles.css ├── Javascripts ├── boroughs_crash_proportions.js ├── bronx_crash_proportions.js ├── brooklyn_crash_proportions.js ├── ccds.js ├── leaflet.label.js ├── main.js ├── manhattan_crash_proportions.js ├── queens_crash_proportions.js └── staten_island_crash_proportions.js ├── Logos ├── Azavea.png └── TransAlt.png ├── README.md └── index.html /CSS/leaflet.label.css: -------------------------------------------------------------------------------- 1 | .leaflet-label { 2 | background: rgb(0, 0, 0); 3 | background: rgba(0, 0, 0, 0.75); 4 | background-clip: padding-box; 5 | border-color: #777; 6 | border-color: rgba(0,0,0,0.25); 7 | border-radius: 4px; 8 | border-style: solid; 9 | border-width: 4px; 10 | color: #111; 11 | display: block; 12 | font: 12px/20px monospace; 13 | font-weight: bold; 14 | padding: 1px 6px; 15 | position: absolute; 16 | -webkit-user-select: none; 17 | -moz-user-select: none; 18 | -ms-user-select: none; 19 | user-select: none; 20 | pointer-events: none; 21 | white-space: nowrap; 22 | z-index: 6; 23 | } 24 | 25 | .leaflet-label.leaflet-clickable { 26 | cursor: pointer; 27 | pointer-events: auto; 28 | } 29 | 30 | .leaflet-label:before, 31 | .leaflet-label:after { 32 | border-top: 6px solid transparent; 33 | border-bottom: 6px solid transparent; 34 | content: none; 35 | position: absolute; 36 | top: 5px; 37 | } 38 | 39 | .leaflet-label:before { 40 | border-right: 6px solid black; 41 | border-right-color: inherit; 42 | left: -10px; 43 | } 44 | 45 | .leaflet-label:after { 46 | border-left: 6px solid black; 47 | border-left-color: inherit; 48 | right: -10px; 49 | } 50 | 51 | .leaflet-label-right:before, 52 | .leaflet-label-left:after { 53 | content: ""; 54 | } -------------------------------------------------------------------------------- /CSS/styles.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | #map { 8 | height: 93%; 9 | width: 75%; 10 | top: 7%; 11 | bottom: 0; 12 | left: 25%; 13 | padding: 0; 14 | margin: 0; 15 | } 16 | 17 | #map .cartodb-legend-stack { 18 | width: 30%; 19 | position: absolute; 20 | background: rgba(0, 0, 0, 0.8); 21 | } 22 | 23 | #titlebar-one { 24 | height: 7%; 25 | width: 25%; 26 | left: 25%; 27 | position: absolute; 28 | top: 0; 29 | background: white; 30 | overflow: hidden; 31 | display: table; 32 | text-align: center; 33 | } 34 | 35 | 36 | #titlebar-two { 37 | height: 7%; 38 | width: 50%; 39 | position: absolute; 40 | top: 0; 41 | background: black; 42 | left: 50%; 43 | overflow: hidden; 44 | display: table; 45 | } 46 | 47 | .title-one { 48 | font-family: "HelveticaNeue-Light", Helvetica, sans-serif; 49 | font-size: 110%; 50 | color: #284a59; 51 | overflow: hidden; 52 | white-space: nowrap; 53 | display: table-cell; 54 | vertical-align: middle; 55 | } 56 | 57 | .title-two { 58 | font-family: "HelveticaNeue-Light", Helvetica, sans-serif; 59 | font-size: 110%; 60 | color: #ffffff; 61 | padding-left: 5%; 62 | overflow: hidden; 63 | white-space: nowrap; 64 | display: table-cell; 65 | vertical-align: middle; 66 | } 67 | 68 | #about { 69 | height: 60px; 70 | width: 20px; 71 | top: 20%; 72 | left: 25%; 73 | background: black; 74 | border-top-right-radius: 5px; 75 | border-bottom-right-radius: 5px; 76 | border-top: 1px solid white; 77 | border-bottom: 1px solid white; 78 | border-right: 1px solid white; 79 | position: absolute; 80 | white-space: nowrap; 81 | overflow: hidden; 82 | display: table; 83 | cursor: pointer; 84 | } 85 | 86 | #about-title { 87 | font-size: 10px; 88 | color: white; 89 | font-family: "HelveticaNeue-Bold", Helvetica, sans-serif; 90 | transform: rotate(90deg); 91 | transform-origin: right, top; 92 | position: relative; 93 | display: table-cell; 94 | vertical-align: middle; 95 | text-align: center; 96 | cursor: pointer; 97 | } 98 | 99 | #about-inset { 100 | height: 285px; 101 | width: 175px; 102 | top: 20%; 103 | left: 25%; 104 | background: black; 105 | border-top-right-radius: 5px; 106 | border-bottom-right-radius: 5px; 107 | border-top: 1px solid white; 108 | border-bottom: 1px solid white; 109 | border-right: 1px solid white; 110 | position: absolute; 111 | overflow: hidden; 112 | cursor: pointer; 113 | visibility: hidden; 114 | } 115 | 116 | #about-text { 117 | font-size: 10px; 118 | color: white; 119 | font-family: "HelveticaNeue-Light", Helvetica, sans-serif; 120 | position: relative; 121 | padding: 5px, 5px, 5px, 5px; 122 | visibility: hidden; 123 | } 124 | 125 | #dashboard { 126 | top: 7%; 127 | width: 25%; 128 | height: 93%; 129 | padding: 0; 130 | margin: 0; 131 | background: black; 132 | position: absolute; 133 | bottom: 0; 134 | overflow: auto; 135 | border-right: 1px solid white; 136 | } 137 | 138 | #dashboard-title-container { 139 | height: 10%; 140 | width: 100%; 141 | background: black; 142 | } 143 | 144 | #dashboard-title { 145 | color: white; 146 | font-family: "HelveticaNeue-Light", Helvetica, sans-serif; 147 | font-size: 110%, 18px; 148 | padding-top: 5%; 149 | padding-left: 10%; 150 | padding-right: 10%; 151 | margin-left: 20px; 152 | overflow: hidden; 153 | white-space: nowrap; 154 | } 155 | 156 | #selector_container { 157 | position: absolute; 158 | width: 100%; 159 | top: 10%; 160 | height: 10%; 161 | overflow: hidden; 162 | white-space: nowrap; 163 | } 164 | 165 | #selector_menu { 166 | position: absolute; 167 | top: 0; 168 | left: 7%; 169 | z-index: 9000; 170 | width: 100%; 171 | height: 100%; 172 | } 173 | 174 | label { 175 | color: white; 176 | font-family: "HelveticaNeue-Light", Helvetica, sans-serif; 177 | font-size: 12px; 178 | overflow: hidden; 179 | white-space: nowrap; 180 | text-align: right; 181 | float:left; 182 | clear: both; 183 | } 184 | 185 | .selector { 186 | background: black; 187 | color: white; 188 | overflow: hidden; 189 | white-space: nowrap; 190 | float: left; 191 | margin-right: 10%; 192 | clear:both; 193 | } 194 | 195 | #d3-elements { 196 | position: absolute; 197 | width: 100%; 198 | top: 20%; 199 | height: 80%; 200 | } 201 | 202 | .legend_template { 203 | height: auto; 204 | width: auto; 205 | } 206 | 207 | .bubbles { 208 | color: rgba(0,0,0,0.8); 209 | } 210 | 211 | 212 | #photo { 213 | height: 7%; 214 | width: 25%; 215 | position: absolute; 216 | top: 0; 217 | left: 0; 218 | overflow: hidden; 219 | display: inline-block; 220 | background: white; 221 | } 222 | 223 | .logo { 224 | height: 100%; 225 | width: auto; 226 | overflow: hidden; 227 | } 228 | 229 | .leaflet-label { 230 | background: #1E1E1E; 231 | color: #fff; 232 | font-family: "HelveticaNeue-Light", Helvetica, sans-serif; 233 | font-size: 12px; 234 | width: 175px; 235 | border-radius: 5px; 236 | padding: 5px 5px 5px 5px; 237 | border: 5px solid rgba(255,255,255,0.25); 238 | } 239 | 240 | .leaflet-label p { 241 | margin: 0; 242 | padding: 0; 243 | text-align: right; 244 | float: right; 245 | font-family: "HelveticaNeue-Bold", Helvetica, sans-serif; 246 | font-size: 12px; 247 | } 248 | 249 | u { 250 | border-bottom: 1px dotted #fff; 251 | text-decoration: none; 252 | } 253 | 254 | .axis { 255 | stroke: white; 256 | stroke-width: 1; 257 | fill: none; 258 | } 259 | 260 | .x axis { 261 | stroke: white; 262 | stroke-width: 1; 263 | fill: none; 264 | } 265 | 266 | .y axis { 267 | stroke: white; 268 | stroke-width: 1; 269 | fill: none; 270 | } 271 | 272 | .axis text { 273 | font-family: "HelveticaNeue-Light", Helvetica, sans-serif; 274 | font-size: 8px; 275 | } 276 | 277 | #scatterplot-title { 278 | font-family: monospace, Helvetica, sans-serif; 279 | font-size: 10px; 280 | fill: white; 281 | text-rendering: geometricPrecision; 282 | } 283 | 284 | #circle-title { 285 | right: 20px; 286 | bottom: 130px; 287 | height: 20px; 288 | width: 100px; 289 | position: absolute; 290 | } 291 | 292 | #circle-max { 293 | right: 20px; 294 | bottom: 20px; 295 | width: 100px; 296 | height: 100px; 297 | background: rgba(255,140,0,0.75); 298 | -moz-border-radius: 50px; 299 | -webkit-border-radius: 50px; 300 | border-radius: 50px; 301 | border: 1px solid white; 302 | position: absolute; 303 | visibility: hidden; 304 | } 305 | 306 | #circle-min { 307 | right: 48px; 308 | bottom: 20px; 309 | width: 42px; 310 | height: 42px; 311 | background: rgba(152,171,197,0.75); 312 | -moz-border-radius: 24px; 313 | -webkit-border-radius: 24px; 314 | border-radius: 50px; 315 | border: 1px solid white; 316 | position: absolute; 317 | z-index: 9000; 318 | visibility: hidden; 319 | } 320 | 321 | .circle-text { 322 | font-size: 10px; 323 | font-family: "HelveticaNeue-Bold", Helvetica, sans-serif; 324 | color: white; 325 | text-align: center; 326 | visibility: hidden; 327 | } 328 | 329 | .svg-container { 330 | display: inline-block; 331 | position: relative; 332 | width: 100%; 333 | } 334 | 335 | .svg-container-2 { 336 | display: inline-block; 337 | position: relative; 338 | width: 100%; 339 | top: 47.5%; 340 | } 341 | 342 | .svg-content-responsive { 343 | display: inline-block; 344 | position: absolute; 345 | } 346 | -------------------------------------------------------------------------------- /Javascripts/boroughs_crash_proportions.js: -------------------------------------------------------------------------------- 1 | var boroughs = [ 2 | { 3 | "name": "Bronx", 4 | "crash_dens": 148.7 5 | }, 6 | { 7 | "name": "Brooklyn", 8 | "crash_dens": 217.2 9 | }, 10 | { 11 | "name": "Manhattan", 12 | "crash_dens": 498.6 13 | }, 14 | { 15 | "name": "Queens", 16 | "crash_dens": 92.1 17 | }, 18 | { 19 | "name": "Staten Island", 20 | "crash_dens": 22.95 21 | } 22 | ]; -------------------------------------------------------------------------------- /Javascripts/bronx_crash_proportions.js: -------------------------------------------------------------------------------- 1 | var bronx = [ 2 | { 3 | "name": "Dis. 11", 4 | "crash_dens": 76.3 5 | }, 6 | { 7 | "name": "Dis. 12", 8 | "crash_dens": 123.5 9 | }, 10 | { 11 | "name": "Dis. 13", 12 | "crash_dens": 55.8 13 | }, 14 | { 15 | "name": "Dis. 14", 16 | "crash_dens": 380.5 17 | }, 18 | { 19 | "name": "Dis. 15", 20 | "crash_dens": 270.2 21 | }, 22 | { 23 | "name": "Dis. 16", 24 | "crash_dens": 310.8 25 | }, 26 | { 27 | "name": "Dis. 17", 28 | "crash_dens": 220.7 29 | }, 30 | { 31 | "name": "Dis. 18", 32 | "crash_dens": 169.8 33 | } 34 | ]; 35 | -------------------------------------------------------------------------------- /Javascripts/brooklyn_crash_proportions.js: -------------------------------------------------------------------------------- 1 | var brooklyn = [ 2 | { 3 | "name": "Dis. 33", 4 | "crash_dens": 293 5 | }, 6 | { 7 | "name": "Dis. 34", 8 | "crash_dens": 318.7 9 | }, 10 | { 11 | "name": "Dis. 35", 12 | "crash_dens": 385.8 13 | }, 14 | { 15 | "name": "Dis. 36", 16 | "crash_dens": 446.6 17 | }, 18 | { 19 | "name": "Dis. 37", 20 | "crash_dens": 243.1 21 | }, 22 | { 23 | "name": "Dis. 38", 24 | "crash_dens": 178.8 25 | }, 26 | { 27 | "name": "Dis. 39", 28 | "crash_dens": 230 29 | }, 30 | { 31 | "name": "Dis. 40", 32 | "crash_dens": 438.4 33 | }, 34 | { 35 | "name": "Dis. 41", 36 | "crash_dens": 400.6 37 | }, 38 | { 39 | "name": "Dis. 42", 40 | "crash_dens": 116.7 41 | }, 42 | { 43 | "name": "Dis. 43", 44 | "crash_dens": 123.8 45 | }, 46 | { 47 | "name": "Dis. 44", 48 | "crash_dens": 255.8 49 | }, 50 | { 51 | "name": "Dis. 45", 52 | "crash_dens": 227.5 53 | }, 54 | { 55 | "name": "Dis. 46", 56 | "crash_dens": 65.2 57 | }, 58 | { 59 | "name": "Dis. 47", 60 | "crash_dens": 163.4 61 | }, 62 | { 63 | "name": "Dis. 48", 64 | "crash_dens": 219.7 65 | } 66 | ]; -------------------------------------------------------------------------------- /Javascripts/leaflet.label.js: -------------------------------------------------------------------------------- 1 | /* 2 | Leaflet.label, a plugin that adds labels to markers and vectors for Leaflet powered maps. 3 | (c) 2012-2013, Jacob Toye, Smartrak 4 | 5 | https://github.com/Leaflet/Leaflet.label 6 | http://leafletjs.com 7 | https://github.com/jacobtoye 8 | */ 9 | (function(t){var e=t.L;e.labelVersion="0.2.2-dev",e.Label=e.Class.extend({includes:e.Mixin.Events,options:{className:"",clickable:!1,direction:"right",noHide:!1,offset:[12,-15],opacity:1,zoomAnimation:!0},initialize:function(t,i){e.setOptions(this,t),this._source=i,this._animated=e.Browser.any3d&&this.options.zoomAnimation,this._isOpen=!1},onAdd:function(t){this._map=t,this._pane=this.options.pane?t._panes[this.options.pane]:this._source instanceof e.Marker?t._panes.markerPane:t._panes.popupPane,this._container||this._initLayout(),this._pane.appendChild(this._container),this._initInteraction(),this._update(),this.setOpacity(this.options.opacity),t.on("moveend",this._onMoveEnd,this).on("viewreset",this._onViewReset,this),this._animated&&t.on("zoomanim",this._zoomAnimation,this),e.Browser.touch&&!this.options.noHide&&(e.DomEvent.on(this._container,"click",this.close,this),t.on("click",this.close,this))},onRemove:function(t){this._pane.removeChild(this._container),t.off({zoomanim:this._zoomAnimation,moveend:this._onMoveEnd,viewreset:this._onViewReset},this),this._removeInteraction(),this._map=null},setLatLng:function(t){return this._latlng=e.latLng(t),this._map&&this._updatePosition(),this},setContent:function(t){return this._previousContent=this._content,this._content=t,this._updateContent(),this},close:function(){var t=this._map;t&&(e.Browser.touch&&!this.options.noHide&&(e.DomEvent.off(this._container,"click",this.close),t.off("click",this.close,this)),t.removeLayer(this))},updateZIndex:function(t){this._zIndex=t,this._container&&this._zIndex&&(this._container.style.zIndex=t)},setOpacity:function(t){this.options.opacity=t,this._container&&e.DomUtil.setOpacity(this._container,t)},_initLayout:function(){this._container=e.DomUtil.create("div","leaflet-label "+this.options.className+" leaflet-zoom-animated"),this.updateZIndex(this._zIndex)},_update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updatePosition(),this._container.style.visibility="")},_updateContent:function(){this._content&&this._map&&this._prevContent!==this._content&&"string"==typeof this._content&&(this._container.innerHTML=this._content,this._prevContent=this._content,this._labelWidth=this._container.offsetWidth)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},_setPosition:function(t){var i=this._map,n=this._container,o=i.latLngToContainerPoint(i.getCenter()),s=i.layerPointToContainerPoint(t),a=this.options.direction,l=this._labelWidth,h=e.point(this.options.offset);"right"===a||"auto"===a&&s.xn;n++)e.DomEvent.on(t,i[n],this._fireMouseEvent,this)}},_removeInteraction:function(){if(this.options.clickable){var t=this._container,i=["dblclick","mousedown","mouseover","mouseout","contextmenu"];e.DomUtil.removeClass(t,"leaflet-clickable"),e.DomEvent.off(t,"click",this._onMouseClick,this);for(var n=0;i.length>n;n++)e.DomEvent.off(t,i[n],this._fireMouseEvent,this)}},_onMouseClick:function(t){this.hasEventListeners(t.type)&&e.DomEvent.stopPropagation(t),this.fire(t.type,{originalEvent:t})},_fireMouseEvent:function(t){this.fire(t.type,{originalEvent:t}),"contextmenu"===t.type&&this.hasEventListeners(t.type)&&e.DomEvent.preventDefault(t),"mousedown"!==t.type?e.DomEvent.stopPropagation(t):e.DomEvent.preventDefault(t)}}),e.BaseMarkerMethods={showLabel:function(){return this.label&&this._map&&(this.label.setLatLng(this._latlng),this._map.showLabel(this.label)),this},hideLabel:function(){return this.label&&this.label.close(),this},setLabelNoHide:function(t){this._labelNoHide!==t&&(this._labelNoHide=t,t?(this._removeLabelRevealHandlers(),this.showLabel()):(this._addLabelRevealHandlers(),this.hideLabel()))},bindLabel:function(t,i){var n=this.options.icon?this.options.icon.options.labelAnchor:this.options.labelAnchor,o=e.point(n)||e.point(0,0);return o=o.add(e.Label.prototype.options.offset),i&&i.offset&&(o=o.add(i.offset)),i=e.Util.extend({offset:o},i),this._labelNoHide=i.noHide,this.label||(this._labelNoHide||this._addLabelRevealHandlers(),this.on("remove",this.hideLabel,this).on("move",this._moveLabel,this).on("add",this._onMarkerAdd,this),this._hasLabelHandlers=!0),this.label=new e.Label(i,this).setContent(t),this},unbindLabel:function(){return this.label&&(this.hideLabel(),this.label=null,this._hasLabelHandlers&&(this._labelNoHide||this._removeLabelRevealHandlers(),this.off("remove",this.hideLabel,this).off("move",this._moveLabel,this).off("add",this._onMarkerAdd,this)),this._hasLabelHandlers=!1),this},updateLabelContent:function(t){this.label&&this.label.setContent(t)},getLabel:function(){return this.label},_onMarkerAdd:function(){this._labelNoHide&&this.showLabel()},_addLabelRevealHandlers:function(){this.on("mouseover",this.showLabel,this).on("mouseout",this.hideLabel,this),e.Browser.touch&&this.on("click",this.showLabel,this)},_removeLabelRevealHandlers:function(){this.off("mouseover",this.showLabel,this).off("mouseout",this.hideLabel,this),e.Browser.touch&&this.off("click",this.showLabel,this)},_moveLabel:function(t){this.label.setLatLng(t.latlng)}},e.Icon.Default.mergeOptions({labelAnchor:new e.Point(9,-20)}),e.Marker.mergeOptions({icon:new e.Icon.Default}),e.Marker.include(e.BaseMarkerMethods),e.Marker.include({_originalUpdateZIndex:e.Marker.prototype._updateZIndex,_updateZIndex:function(t){var e=this._zIndex+t;this._originalUpdateZIndex(t),this.label&&this.label.updateZIndex(e)},_originalSetOpacity:e.Marker.prototype.setOpacity,setOpacity:function(t,e){this.options.labelHasSemiTransparency=e,this._originalSetOpacity(t)},_originalUpdateOpacity:e.Marker.prototype._updateOpacity,_updateOpacity:function(){var t=0===this.options.opacity?0:1;this._originalUpdateOpacity(),this.label&&this.label.setOpacity(this.options.labelHasSemiTransparency?this.options.opacity:t)},_originalSetLatLng:e.Marker.prototype.setLatLng,setLatLng:function(t){return this.label&&!this._labelNoHide&&this.hideLabel(),this._originalSetLatLng(t)}}),e.CircleMarker.mergeOptions({labelAnchor:new e.Point(0,0)}),e.CircleMarker.include(e.BaseMarkerMethods),e.Path.include({bindLabel:function(t,i){return this.label&&this.label.options===i||(this.label=new e.Label(i,this)),this.label.setContent(t),this._showLabelAdded||(this.on("mouseover",this._showLabel,this).on("mousemove",this._moveLabel,this).on("mouseout remove",this._hideLabel,this),e.Browser.touch&&this.on("click",this._showLabel,this),this._showLabelAdded=!0),this},unbindLabel:function(){return this.label&&(this._hideLabel(),this.label=null,this._showLabelAdded=!1,this.off("mouseover",this._showLabel,this).off("mousemove",this._moveLabel,this).off("mouseout remove",this._hideLabel,this)),this},updateLabelContent:function(t){this.label&&this.label.setContent(t)},_showLabel:function(t){this.label.setLatLng(t.latlng),this._map.showLabel(this.label)},_moveLabel:function(t){this.label.setLatLng(t.latlng)},_hideLabel:function(){this.label.close()}}),e.Map.include({showLabel:function(t){return this.addLayer(t)}}),e.FeatureGroup.include({clearLayers:function(){return this.unbindLabel(),this.eachLayer(this.removeLayer,this),this},bindLabel:function(t,e){return this.invoke("bindLabel",t,e)},unbindLabel:function(){return this.invoke("unbindLabel")},updateLabelContent:function(t){this.invoke("updateLabelContent",t)}})})(this,document); -------------------------------------------------------------------------------- /Javascripts/main.js: -------------------------------------------------------------------------------- 1 | window.onload = function (){ 2 | 3 | 4 | // when user clicks the about button, show the text 5 | $("#about").on("click", function(){ 6 | $("#about").hide(); 7 | $("#about-inset").css('visibility', 'visible'); 8 | $("#about-text").css('visibility', 'visible'); 9 | }); 10 | 11 | // when user clicks on about inset, show just the about button 12 | $("#about-inset").on("click", function(){ 13 | $("#about-inset").css('visibility', 'hidden'); 14 | $("#about-text").css('visibility', 'hidden'); 15 | $("#about").show(); 16 | }); 17 | 18 | // place the names of your PostGIS tables in a variable 19 | var crashTable = "transalt_crash_data"; 20 | var comdistTable = "ccds_all_data"; 21 | 22 | // create the heatmap 23 | var torqueSource = { 24 | type: 'torque', 25 | options: { 26 | user_name: 'transaltsummer2016', 27 | table_name: crashTable, 28 | cartocss: $("#torque").text() 29 | } 30 | } 31 | 32 | // create the other choropleth layers in an array 33 | var layerSource = { 34 | user_name: 'transaltsummer2016', 35 | type: 'cartodb', 36 | sublayers: [{ 37 | sql: "SELECT * FROM " + comdistTable, 38 | cartocss: "", 39 | interactivity: "cartodb_id" 40 | }] 41 | } 42 | 43 | // set the initial lat / long and zoom level 44 | var locationOptions = { 45 | center: [40.738850, -73.889837], 46 | zoom: 12, 47 | minZoom: 10 48 | }; 49 | 50 | // instantiate map on the 'map' div element 51 | var map = new L.map('map', locationOptions); 52 | 53 | // add a basemap to the 'map' element that was just instantiated 54 | L.tileLayer('https://a.tiles.mapbox.com/v4/mapbox.dark/{z}/{x}/{y}.png?access_token={token}', { 55 | attribution: 'Mapbox', 56 | subdomains: ['a','b','c','d'], 57 | token: 'pk.eyJ1IjoicHppZWdsZXIiLCJhIjoiY2ltMHo3OGRxMDh0MXR5a3JrdHNqaGQ0bSJ9.KAFBMeyysBLz4Ty-ltXVQQ' 58 | }).addTo(map); 59 | 60 | // legends for each layer 61 | 62 | // crashes per square mile 63 | var areaLegend = new cdb.geo.ui.Legend({ 64 | type: "custom", 65 | show_title: false, 66 | title: "", 67 | template: $('#area_legend').html(), 68 | visible: true 69 | }); 70 | 71 | var areaStacked = new cdb.geo.ui.StackedLegend({ 72 | legends: [areaLegend] 73 | }); 74 | 75 | // population density 76 | var densityLegend = new cdb.geo.ui.Legend({ 77 | type: "custom", 78 | show_title: false, 79 | title: "", 80 | template: $('#density_legend').html(), 81 | visible: true 82 | }); 83 | 84 | var densityStacked = new cdb.geo.ui.StackedLegend({ 85 | legends: [densityLegend] 86 | }); 87 | 88 | // poverty rate 89 | var povertyLegend = new cdb.geo.ui.Legend({ 90 | type: "custom", 91 | show_title: false, 92 | title: "", 93 | template: $('#poverty_legend').html(), 94 | visible: true 95 | }); 96 | 97 | var povertyStacked = new cdb.geo.ui.StackedLegend({ 98 | legends: [povertyLegend] 99 | }); 100 | 101 | // unemployment rate 102 | var unemployLegend = new cdb.geo.ui.Legend({ 103 | type: "custom", 104 | show_title: false, 105 | title: "", 106 | template: $('#unemployment_legend').html(), 107 | visible: true 108 | }); 109 | 110 | var unemployStacked = new cdb.geo.ui.StackedLegend({ 111 | legends: [unemployLegend] 112 | }); 113 | 114 | // create the layers and wire them to click events 115 | 116 | cartodb.createLayer(map, layerSource, {legends: true, https: true}) 117 | .addTo(map) 118 | .done(function(layer){ 119 | 120 | var sublayer = layer.getSubLayer(0); 121 | 122 | // function to control CartoCSS from dropdown 123 | var LayerActions = { 124 | intensity: function(){ 125 | areaStacked.hide() 126 | densityStacked.hide() 127 | povertyStacked.hide() 128 | unemployStacked.hide() 129 | $("#circle-max").css('visibility', 'hidden'); 130 | $("#circle-min").css('visibility', 'hidden'); 131 | $(".circle-text").css('visibility', 'hidden'); 132 | sublayer.hide(); 133 | }, 134 | area: function(){ 135 | densityStacked.hide() 136 | povertyStacked.hide() 137 | unemployStacked.hide() 138 | $("#circle-max").css('visibility', 'hidden'); 139 | $("#circle-min").css('visibility', 'hidden'); 140 | $(".circle-text").css('visibility', 'hidden'); 141 | $('#map').append(areaStacked.render().el); 142 | sublayer.show(); 143 | sublayer.setCartoCSS($("#choropleth-area").text()); 144 | return true; 145 | }, 146 | popdensity: function(){ 147 | areaStacked.hide() 148 | povertyStacked.hide() 149 | unemployStacked.hide() 150 | $("#circle-max").css('visibility', 'hidden'); 151 | $("#circle-min").css('visibility', 'hidden'); 152 | $(".circle-text").css('visibility', 'hidden'); 153 | $('#map').append(densityStacked.render().el); 154 | sublayer.show(); 155 | sublayer.setCartoCSS($("#choropleth-density").text()); 156 | return true; 157 | }, 158 | medincome: function(){ 159 | areaStacked.hide() 160 | densityStacked.hide() 161 | povertyStacked.hide() 162 | unemployStacked.hide() 163 | $("#circle-max").css('visibility', 'visible'); 164 | $("#circle-min").css('visibility', 'visible'); 165 | $(".circle-text").css('visibility', 'visible'); 166 | sublayer.show(); 167 | sublayer.setCartoCSS($("#choropleth-bubble").text()); 168 | return true; 169 | }, 170 | povrate: function(){ 171 | unemployStacked.hide() 172 | areaStacked.hide() 173 | densityStacked.hide() 174 | $("#circle-max").css('visibility', 'hidden'); 175 | $("#circle-min").css('visibility', 'hidden'); 176 | $(".circle-text").css('visibility', 'hidden'); 177 | $('#map').append(povertyStacked.render().el); 178 | sublayer.show(); 179 | sublayer.setCartoCSS($("#choropleth-povrate").text()); 180 | return true; 181 | }, 182 | unemprate: function(){ 183 | areaStacked.hide() 184 | densityStacked.hide() 185 | povertyStacked.hide() 186 | $("#circle-max").css('visibility', 'hidden'); 187 | $("#circle-min").css('visibility', 'hidden'); 188 | $(".circle-text").css('visibility', 'hidden'); 189 | $('#map').append(unemployStacked.render().el); 190 | sublayer.show(); 191 | sublayer.setCartoCSS($("#choropleth-unemployment").text()); 192 | return true; 193 | } 194 | }; 195 | 196 | $('#layer_selector').change(function() { 197 | LayerActions[$(this).val()](); 198 | }); 199 | }); 200 | 201 | // show the heat map on load 202 | cartodb.createLayer(map, torqueSource, {legends: true}) 203 | .addTo(map) 204 | .done(function(layer){ 205 | 206 | // store the heat map layer in a variable 207 | var heatMap = layer; 208 | 209 | // function to control CartoCSS from dropdown 210 | var LayerActions = { 211 | intensity: function(){ 212 | map.addLayer(heatMap); 213 | }, 214 | area: function(){ 215 | map.removeLayer(heatMap); 216 | }, 217 | popdensity: function(){ 218 | map.removeLayer(heatMap); 219 | }, 220 | medincome: function(){ 221 | map.removeLayer(heatMap); 222 | }, 223 | povrate: function(){ 224 | map.removeLayer(heatMap); 225 | }, 226 | unemprate: function(){ 227 | map.removeLayer(heatMap); 228 | } 229 | }; 230 | 231 | $('#layer_selector').change(function() { 232 | LayerActions[$(this).val()](); 233 | }); 234 | }) 235 | 236 | // wire the borough buttons to zoom events 237 | var ZoomActions = { 238 | reset: function(){ 239 | map.setView([40.738850, -73.889837], 12); 240 | }, 241 | bronx: function(){ 242 | var bronxXY = [40.836653, -73.922238]; 243 | map.setView(bronxXY, 13); 244 | }, 245 | brooklyn: function(){ 246 | var brooklynXY = [40.680862, -73.965410]; 247 | map.setView(brooklynXY, 13); 248 | }, 249 | manhattan: function(){ 250 | var manhattanXY = [40.740671, -73.988929]; 251 | map.setView(manhattanXY, 13); 252 | }, 253 | queens: function(){ 254 | var queensXY = [40.754, -73.898635]; 255 | map.setView(queensXY, 13); 256 | }, 257 | statenisland: function(){ 258 | var statenislandXY = [40.607092, -74.107118]; 259 | map.setView(statenislandXY, 13); 260 | } 261 | }; 262 | 263 | $('#zoom_selector').change(function(){ 264 | ZoomActions[$(this).val()](); 265 | }); 266 | 267 | // create the GeoJSON layer and tooltips 268 | 269 | // original GeoJSON style 270 | function style(feature) { 271 | return { 272 | weight: 0.5, 273 | opacity: 1, 274 | color: 'white', 275 | fillOpacity: 0, 276 | fillColor: 'white' 277 | }; 278 | } 279 | 280 | // function to highlight GeoJSON features 281 | function highlightFeature(e) { 282 | var layer = e.target; 283 | 284 | layer.setStyle({ 285 | weight: 2, 286 | color: '#fff', 287 | fillOpacity: 0.5 288 | }); 289 | 290 | if (!L.Browser.ie && !L.Browser.opera) { 291 | layer.bringToFront(); 292 | } 293 | } 294 | 295 | var geojson; 296 | 297 | // function to reset style of GeoJSON features 298 | function resetHighlight(e) { 299 | geojson.resetStyle(e.target); 300 | } 301 | 302 | // function to zoom features when clicked 303 | function zoomToFeature(e) { 304 | map.fitBounds(e.target.getBounds()); 305 | } 306 | 307 | 308 | // function bringing all of the listeners together 309 | function onEachFeature(feature, layer) { 310 | layer.on({ 311 | mouseover: highlightFeature, 312 | mouseout: resetHighlight, 313 | click: zoomToFeature 314 | }); 315 | } 316 | 317 | geojson = L.geoJson(ccds, { 318 | style: style, 319 | onEachFeature: onEachFeature 320 | }).addTo(map); 321 | 322 | geojson.eachLayer(function (layer) { 323 | layer.bindLabel('City Council District

' + '' + layer.feature.properties.coun_dist + '


Crashes per Sq. Mi.

' + layer.feature.properties.crash_area.toFixed(1) + '

' + '
Population Density

' + layer.feature.properties.pop_density.toLocaleString('en', {minimumFractionDigits: 0,maximumFractionDigits: 0}) + '

' + '
Poverty Rate (%)

' + layer.feature.properties.pov_rate.toFixed(1) + '


Median Income ($)

' + layer.feature.properties.med_income.toLocaleString('en') + '

' + '
Unemployment Rate (%)

' + layer.feature.properties.unemp_rate.toFixed(1) + '

'); 324 | }); 325 | 326 | // bring in the d3 interactive elements! 327 | 328 | // create a global variable for your data 329 | var data = ccds; 330 | 331 | // set the width, height, and margins of your svg 332 | var margin = {top: 35, right: 35, bottom: 35, left: 35}, 333 | w = 300 - margin.left - margin.right, 334 | h = 225 - margin.top - margin.bottom; 335 | 336 | // load the data 337 | var dataset = data.features; 338 | 339 | var xScale = d3.scale.linear() 340 | .domain([0, d3.max(dataset, function(d){ 341 | return d.properties.pop_density; 342 | })]) 343 | .range([0, w]); 344 | 345 | var yScale = d3.scale.linear() 346 | .domain([0, d3.max(dataset, function(d){ 347 | return d.properties.crash_area; 348 | })]) 349 | .range([h, 0]); 350 | 351 | // create the scatterplot svg 352 | var svg = d3.select("#d3-elements") 353 | .append("div") 354 | .classed("svg-container", true) 355 | .append("svg") 356 | .attr("preserveAspectRatio", "xMinYMin meet") 357 | .attr("viewBox", "0 0 300 225") 358 | .classed("svg-content-responsive", true) 359 | .append("g") 360 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 361 | 362 | // make a scatterplot 363 | svg.selectAll("circle") 364 | .data(dataset) 365 | .enter() 366 | .append("circle") 367 | .attr("cx", function(d){ 368 | return xScale(d.properties.pop_density); 369 | }) 370 | .attr("cy", function(d){ 371 | return yScale(d.properties.crash_area); 372 | }) 373 | .attr("r", function(d){ 374 | return 4; 375 | }) 376 | .attr("fill", "#1DFF84") 377 | .attr("opacity", 0.5); 378 | 379 | // create axes 380 | var xAxis = d3.svg.axis() 381 | .scale(xScale) 382 | .orient("bottom") 383 | .ticks(5) 384 | .innerTickSize(1) 385 | .outerTickSize(0); 386 | 387 | var yAxis = d3.svg.axis() 388 | .scale(yScale) 389 | .orient("right") 390 | .ticks(5) 391 | .innerTickSize(1) 392 | .outerTickSize(0); 393 | 394 | // append the x axis to the svg 395 | svg.append("g") 396 | .attr("class", "x axis") 397 | .attr("transform", "translate(0," + h + ")") 398 | .call(xAxis); 399 | 400 | // append a label to the x axis 401 | svg.append("text") 402 | .attr("id", "xlabel") 403 | .text("Population Density") 404 | .style("text-anchor", "middle") 405 | .attr("fill", "white") 406 | .attr("font-family", "HelveticaNeue-Light, Helvetica, sans-serif") 407 | .attr("font-size", "10px") 408 | .attr("x", w/2) 409 | .attr("y", h + margin.bottom - 5); 410 | 411 | // append the y axis to the svg 412 | svg.append("g") 413 | .attr("class", "y axis") 414 | .attr("transform", "translate(0,0)") 415 | .call(yAxis); 416 | 417 | // append a label to the y axis 418 | svg.append("text") 419 | .text("Crashes per Sq. Mi.") 420 | .style("text-anchor", "middle") 421 | .attr("fill", "white") 422 | .attr("font-family", "HelveticaNeue-Light, Helvetica, sans-serif") 423 | .attr("font-size", "10px") 424 | .attr("x", -80) 425 | .attr("y", -10) 426 | .attr("transform", "rotate(-90)"); 427 | 428 | // append a title 429 | svg.append("text") 430 | .attr("id", "sctitle") 431 | .attr("x", (w / 2)) 432 | .attr("y", 0 - (margin.top / 2)) 433 | .attr("text-anchor", "middle") 434 | .attr("font-size", "12px") 435 | .attr("font-family", "HelveticaNeue-Light, Helvetica, sans-serif") 436 | .attr("fill", "white") 437 | .text("Relating Population Density and Crash Density"); 438 | 439 | // create a function to update the data on layer change 440 | 441 | var D3Actions = { 442 | 443 | intensity: function(){ 444 | 445 | // update x scale 446 | var xScale = d3.scale.linear() 447 | .domain([0, d3.max(dataset, function(d){ 448 | return d.properties.pop_density; 449 | })]) 450 | .range([0, w]); 451 | 452 | // update the circles 453 | svg.selectAll("circle") 454 | .data(dataset) 455 | .transition() 456 | .duration(1500) 457 | .attr("cx", function(d){ 458 | return xScale(d.properties.pop_density); 459 | }) 460 | .attr("cy", function(d){ 461 | return yScale(d.properties.crash_area); 462 | }) 463 | .attr("r", function(d){ 464 | return 4; 465 | }) 466 | .attr("fill", "#1DFF84") 467 | .attr("opacity", 0.5); 468 | 469 | // update x axis variable 470 | var xAxis = d3.svg.axis() 471 | .scale(xScale) 472 | .orient("bottom") 473 | .ticks(5) 474 | .innerTickSize(1) 475 | .outerTickSize(0); 476 | 477 | // update the x axis svg 478 | svg.select(".x.axis") 479 | .transition() 480 | .duration(1500) 481 | .call(xAxis); 482 | 483 | // update the x axis label 484 | svg.select("#xlabel") 485 | .transition() 486 | .duration(3000) 487 | .text("Population Density"); 488 | 489 | // update the title 490 | svg.select("#sctitle") 491 | .transition() 492 | .duration(3000) 493 | .text("Relating Population Density and Crash Density"); 494 | 495 | return true; 496 | }, 497 | area: function(){ 498 | 499 | // update x scale 500 | var xScale = d3.scale.linear() 501 | .domain([0, d3.max(dataset, function(d){ 502 | return d.properties.pop_density; 503 | })]) 504 | .range([0, w]); 505 | 506 | // update the circles 507 | svg.selectAll("circle") 508 | .data(dataset) 509 | .transition() 510 | .duration(1500) 511 | .attr("cx", function(d){ 512 | return xScale(d.properties.pop_density); 513 | }) 514 | .attr("cy", function(d){ 515 | return yScale(d.properties.crash_area); 516 | }) 517 | .attr("r", function(d){ 518 | return 4; 519 | }) 520 | .attr("fill", "#1DFF84") 521 | .attr("opacity", 0.5); 522 | 523 | // update x axis variable 524 | var xAxis = d3.svg.axis() 525 | .scale(xScale) 526 | .orient("bottom") 527 | .ticks(5) 528 | .innerTickSize(1) 529 | .outerTickSize(0); 530 | 531 | // update the x axis svg 532 | svg.select(".x.axis") 533 | .transition() 534 | .duration(1500) 535 | .call(xAxis); 536 | 537 | // update the x axis label 538 | svg.select("#xlabel") 539 | .transition() 540 | .duration(3000) 541 | .text("Population Density"); 542 | 543 | // update the title 544 | svg.select("#sctitle") 545 | .transition() 546 | .duration(3000) 547 | .text("Relating Population Density and Crash Density"); 548 | 549 | return true; 550 | }, 551 | popdensity: function(){ 552 | 553 | // update x scale 554 | var xScale = d3.scale.linear() 555 | .domain([0, d3.max(dataset, function(d){ 556 | return d.properties.pop_density; 557 | })]) 558 | .range([0, w]); 559 | 560 | // update the circles 561 | svg.selectAll("circle") 562 | .data(dataset) 563 | .transition() 564 | .duration(1500) 565 | .attr("cx", function(d){ 566 | return xScale(d.properties.pop_density); 567 | }) 568 | .attr("cy", function(d){ 569 | return yScale(d.properties.crash_area); 570 | }) 571 | .attr("r", function(d){ 572 | return 4; 573 | }) 574 | .attr("fill", "#1DFF84") 575 | .attr("opacity", 0.5); 576 | 577 | // update x axis variable 578 | var xAxis = d3.svg.axis() 579 | .scale(xScale) 580 | .orient("bottom") 581 | .ticks(5) 582 | .innerTickSize(1) 583 | .outerTickSize(0); 584 | 585 | // update the x axis svg 586 | svg.select(".x.axis") 587 | .transition() 588 | .duration(1500) 589 | .call(xAxis); 590 | 591 | // update the x axis label 592 | svg.select("#xlabel") 593 | .transition() 594 | .duration(3000) 595 | .text("Population Density"); 596 | 597 | // update the title 598 | svg.select("#sctitle") 599 | .transition() 600 | .duration(3000) 601 | .text("Relating Population Density and Crash Density"); 602 | 603 | return true; 604 | }, 605 | medincome: function(){ 606 | 607 | // update x scale 608 | var xScale = d3.scale.linear() 609 | .domain([0, d3.max(dataset, function(d){ 610 | return d.properties.med_income; 611 | })]) 612 | .range([0, w]); 613 | 614 | // update the circles 615 | svg.selectAll("circle") 616 | .data(dataset) 617 | .transition() 618 | .duration(1500) 619 | .attr("cx", function(d){ 620 | return xScale(d.properties.med_income); 621 | }) 622 | .attr("cy", function(d){ 623 | return yScale(d.properties.crash_area); 624 | }) 625 | .attr("r", function(d){ 626 | return 4; 627 | }) 628 | .attr("fill", "#1DFF84") 629 | .attr("opacity", 0.5); 630 | 631 | // update x axis variable 632 | var xAxis = d3.svg.axis() 633 | .scale(xScale) 634 | .orient("bottom") 635 | .ticks(5) 636 | .innerTickSize(1) 637 | .outerTickSize(0); 638 | 639 | // update the x axis svg 640 | svg.select(".x.axis") 641 | .transition() 642 | .duration(1500) 643 | .call(xAxis); 644 | 645 | // update the x axis label 646 | svg.select("#xlabel") 647 | .transition() 648 | .duration(3000) 649 | .text("Median Income ($)"); 650 | 651 | // update the title 652 | svg.select("#sctitle") 653 | .transition() 654 | .duration(3000) 655 | .text("Relating Median Income and Crash Density"); 656 | 657 | return true; 658 | }, 659 | povrate: function(){ 660 | 661 | // update x scale 662 | var xScale = d3.scale.linear() 663 | .domain([0, d3.max(dataset, function(d){ 664 | return d.properties.pov_rate; 665 | })]) 666 | .range([0, w]); 667 | 668 | // update the circles 669 | svg.selectAll("circle") 670 | .data(dataset) 671 | .transition() 672 | .duration(1500) 673 | .attr("cx", function(d){ 674 | return xScale(d.properties.pov_rate); 675 | }) 676 | .attr("cy", function(d){ 677 | return yScale(d.properties.crash_area); 678 | }) 679 | .attr("r", function(d){ 680 | return 4; 681 | }) 682 | .attr("fill", "#1DFF84") 683 | .attr("opacity", 0.5); 684 | 685 | // update x axis variable 686 | var xAxis = d3.svg.axis() 687 | .scale(xScale) 688 | .orient("bottom") 689 | .ticks(5) 690 | .innerTickSize(1) 691 | .outerTickSize(0); 692 | 693 | // update the x axis svg 694 | svg.select(".x.axis") 695 | .transition() 696 | .duration(1500) 697 | .call(xAxis); 698 | 699 | // update the x axis label 700 | svg.select("#xlabel") 701 | .transition() 702 | .duration(3000) 703 | .text("Poverty Rate (%)"); 704 | 705 | // update the title 706 | svg.select("#sctitle") 707 | .transition() 708 | .duration(3000) 709 | .text("Relating Poverty Rate and Crash Density"); 710 | 711 | return true; 712 | }, 713 | unemprate: function(){ 714 | 715 | // update x scale 716 | var xScale = d3.scale.linear() 717 | .domain([0, d3.max(dataset, function(d){ 718 | return d.properties.unemp_rate; 719 | })]) 720 | .range([0, w]); 721 | 722 | // update the circles 723 | svg.selectAll("circle") 724 | .data(dataset) 725 | .transition() 726 | .duration(1500) 727 | .attr("cx", function(d){ 728 | return xScale(d.properties.unemp_rate); 729 | }) 730 | .attr("cy", function(d){ 731 | return yScale(d.properties.crash_area); 732 | }) 733 | .attr("r", function(d){ 734 | return 4; 735 | }) 736 | .attr("fill", "#1DFF84") 737 | .attr("opacity", 0.5); 738 | 739 | // update x axis variable 740 | var xAxis = d3.svg.axis() 741 | .scale(xScale) 742 | .orient("bottom") 743 | .ticks(5) 744 | .innerTickSize(1) 745 | .outerTickSize(0); 746 | 747 | // update the x axis svg 748 | svg.select(".x.axis") 749 | .transition() 750 | .duration(1500) 751 | .call(xAxis); 752 | 753 | // update the x axis label 754 | svg.select("#xlabel") 755 | .transition() 756 | .duration(3000) 757 | .text("Unemployment Rate (%)"); 758 | 759 | // update the title 760 | svg.select("#sctitle") 761 | .transition() 762 | .duration(3000) 763 | .text("Relating Unemployment Rate and Crash Density"); 764 | 765 | return true; 766 | } 767 | } 768 | 769 | $('#layer_selector').change(function() { 770 | D3Actions[$(this).val()](); 771 | }); 772 | 773 | // add some space between things 774 | var spacing = d3.select("#d3-elements") 775 | .append("svg") 776 | .attr("width", "100%") 777 | .attr("height", "5%"); 778 | 779 | // now create the bar chart. start by setting width and height of svg are the same. 780 | 781 | // create the dataset 782 | var datasetBar = boroughs; 783 | console.log(datasetBar); 784 | 785 | // create the xScale and yScale 786 | 787 | var xScaleBar = d3.scale.linear() 788 | .domain([0, d3.max(datasetBar, function(d){ 789 | return d.crash_dens; 790 | })]) 791 | .range([0, w]); 792 | 793 | var yScaleBar = d3.scale.ordinal() 794 | .domain(d3.range(datasetBar.length)) 795 | .rangeRoundBands([0, h], 0.05); 796 | 797 | // set a color scale 798 | var color = d3.scale.ordinal() 799 | .range(["#98abc5", "#d0743c", "#ff8c00", "#a05d56", "#8a89a6"]); 800 | 801 | // create the ring chart svg 802 | var svgBar = d3.select("#d3-elements") 803 | .append("div") 804 | .classed("svg-container-2", true) 805 | .append("svg") 806 | .attr("preserveAspectRatio", "xMinYMin meet") 807 | .attr("viewBox", "0 0 300 225") 808 | .classed("svg-content-responsive", true); 809 | 810 | //Create bars 811 | svgBar.selectAll("rect") 812 | .data(datasetBar) 813 | .enter() 814 | .append("rect") 815 | .attr("x", margin.left) 816 | .attr("y", function(d, i) { 817 | return yScaleBar(i) + margin.top/2; 818 | }) 819 | .attr("width", function(d) { 820 | return xScaleBar(d.crash_dens); 821 | }) 822 | .attr("height", yScaleBar.rangeBand()) 823 | .attr("fill", function(d, i) { 824 | return color(i); 825 | }) 826 | .attr("opacity", 1); 827 | 828 | //Create labels 829 | svgBar.selectAll("text") 830 | .data(datasetBar) 831 | .enter() 832 | .append("text") 833 | .text(function(d) { 834 | return d.name + " – " + d.crash_dens; 835 | }) 836 | .attr("x", function(d) { 837 | if (d.crash_dens > 200){ 838 | return xScaleBar(d.crash_dens) - 40; 839 | } 840 | else { 841 | return margin.left + 15; 842 | } 843 | }) 844 | .attr("y", function(d, i) { 845 | return yScaleBar(i) + yScaleBar.rangeBand() / 2 + 3 + margin.top/2; 846 | }) 847 | .attr("font-family", "HelveticaNeue-Bold, Helvetica, sans-serif") 848 | .attr("font-size", "8px") 849 | .attr("fill", "white") 850 | .attr("class", "chart-label"); 851 | 852 | // create axes 853 | var xAxisBar = d3.svg.axis() 854 | .scale(xScaleBar) 855 | .orient("bottom") 856 | .ticks(5) 857 | .innerTickSize(1) 858 | .outerTickSize(0); 859 | 860 | var yAxisBar = d3.svg.axis() 861 | .scale(yScaleBar) 862 | .ticks(0) 863 | .tickFormat("") 864 | .orient("right") 865 | .innerTickSize(1) 866 | .outerTickSize(0); 867 | 868 | // append the x axis to the svg 869 | svgBar.append("g") 870 | .attr("class", "x axis") 871 | .attr("transform", "translate(" + margin.left + "," + (margin.top/2 + h) + ")") 872 | .call(xAxisBar); 873 | 874 | // append a label to the x axis 875 | svgBar.append("text") 876 | .text("Crashes per Square Mile (2013-2015)") 877 | .style("text-anchor", "middle") 878 | .attr("fill", "white") 879 | .attr("font-family", "HelveticaNeue-Light, Helvetica, sans-serif") 880 | .attr("font-size", "10px") 881 | .attr("x", w/2 + margin.left) 882 | .attr("y", h + margin.bottom + 15); 883 | 884 | // append the y axis to the svg 885 | svgBar.append("g") 886 | .attr("class", "y axis") 887 | .attr("transform", "translate(" + margin.left + "," + margin.top/2 +")") 888 | .call(yAxisBar); 889 | 890 | // append a label to the y axis 891 | svgBar.append("text") 892 | .text("Borough") 893 | .attr("id", "ylabel") 894 | .style("text-anchor", "middle") 895 | .attr("fill", "white") 896 | .attr("font-family", "HelveticaNeue-Light, Helvetica, sans-serif") 897 | .attr("font-size", "10px") 898 | .attr("x", 80) 899 | .attr("y", 10) 900 | .attr("transform", "rotate(-90)"); 901 | 902 | // append a title 903 | svgBar.append("text") 904 | .text("Crash Density by Borough") 905 | .attr("id", "bar-title") 906 | .attr("x", w/2 + margin.left) 907 | .attr("y", 10) 908 | .attr("text-anchor", "middle") 909 | .attr("font-size", "12px") 910 | .attr("font-family", "HelveticaNeue-Light, Helvetica, sans-serif") 911 | .attr("fill", "white"); 912 | 913 | // change the bar graph on change of zoom 914 | 915 | var D3BarActions = { 916 | 917 | bronx: function(){ 918 | 919 | // update the dataset 920 | var datasetBar = bronx; 921 | 922 | // update the color function 923 | var colors = ["#8a89a6", "#98abc5", "#a05d56", "#d0743c", "#ff8c00"]; 924 | 925 | var colorRange = d3.scale.linear() 926 | .domain(d3.range(0, 1, 1.0 / (colors.length - 1))) 927 | .range(colors); 928 | 929 | var c = d3.scale.linear() 930 | .domain([0, d3.max(datasetBar, function(d){ 931 | return d.crash_dens; 932 | })]) 933 | .range([0,1]); 934 | 935 | // update the scales 936 | xScaleBar.domain([0, d3.max(datasetBar, function(d){ 937 | return d.crash_dens; 938 | })]); 939 | yScaleBar.domain(d3.range(datasetBar.length)); 940 | 941 | // update the axes 942 | var xAxisBar = d3.svg.axis() 943 | .scale(xScaleBar) 944 | .orient("bottom") 945 | .ticks(5) 946 | .innerTickSize(1) 947 | .outerTickSize(0); 948 | 949 | var yAxisBar = d3.svg.axis() 950 | .scale(yScaleBar) 951 | .ticks(0) 952 | .tickFormat("") 953 | .orient("right") 954 | .innerTickSize(1) 955 | .outerTickSize(0); 956 | 957 | // transition in the newby 958 | svgBar.select(".x.axis") 959 | .transition() 960 | .duration(1500) 961 | .call(xAxisBar); 962 | 963 | svgBar.select(".y.axis") 964 | .transition() 965 | .duration(1500) 966 | .call(yAxisBar); 967 | 968 | // select the rects and append the data 969 | var bars = svgBar.selectAll("rect") 970 | .data(datasetBar); 971 | 972 | // add new values at the bottom of the chart 973 | bars.enter() 974 | .append("rect") 975 | .attr("x", margin.left) 976 | .attr("y", h) 977 | .attr("width", function(d) { 978 | return xScaleBar(d.crash_dens); 979 | }) 980 | .attr("height", yScaleBar.rangeBand()) 981 | .attr("opacity", 0); 982 | 983 | // update... 984 | bars.transition() 985 | .duration(1000) 986 | .attr("x", margin.left) 987 | .attr("y", function(d, i) { 988 | return yScaleBar(i) + margin.top/2; 989 | }) 990 | .attr("width", function(d) { 991 | return xScaleBar(d.crash_dens); 992 | }) 993 | .attr("height", yScaleBar.rangeBand()) 994 | .attr("fill", function(d) { 995 | return colorRange(c(d.crash_dens)); 996 | }) 997 | .attr("opacity", 1); 998 | 999 | // exit... 1000 | bars.exit() 1001 | .transition() 1002 | .duration(1000) 1003 | .attr("opacity", -yScaleBar.rangeBand) 1004 | .remove(); 1005 | 1006 | // select the labels 1007 | var labels = svgBar.selectAll(".chart-label") 1008 | .data(datasetBar); 1009 | 1010 | // get them ready 1011 | labels.enter() 1012 | .append("text") 1013 | .attr("x", margin.left) 1014 | .attr("y", (margin.top + h)) 1015 | .attr("font-family", "HelveticaNeue-Bold, Helvetica, sans-serif") 1016 | .attr("font-size", "8px") 1017 | .attr("fill", "white") 1018 | .attr("class", "chart-label"); 1019 | 1020 | // update 1021 | labels.transition() 1022 | .duration(1000) 1023 | .text(function(d) { 1024 | return d.name + " – " + d.crash_dens; 1025 | }) 1026 | .attr("x", function(d) { 1027 | if (d.crash_dens > 150){ 1028 | return xScaleBar(d.crash_dens) - 30; 1029 | } 1030 | else { 1031 | return margin.left + 15; 1032 | } 1033 | }) 1034 | .attr("y", function(d, i) { 1035 | return yScaleBar(i) + yScaleBar.rangeBand() / 2 + 3 + margin.top/2; 1036 | }); 1037 | 1038 | // exit 1039 | labels.exit() 1040 | .transition() 1041 | .duration(1000) 1042 | .attr("opacity", 0) 1043 | .remove(); 1044 | 1045 | // now just change the title 1046 | svgBar.select("#bar-title") 1047 | .text("Crash Densities in Bronx City Council Districts"); 1048 | 1049 | }, 1050 | brooklyn: function(){ 1051 | // update the dataset 1052 | var datasetBar = brooklyn; 1053 | 1054 | // update the color function 1055 | var colors = ["#8a89a6", "#98abc5", "#a05d56", "#d0743c", "#ff8c00"]; 1056 | 1057 | var colorRange = d3.scale.linear() 1058 | .domain(d3.range(0, 1, 1.0 / (colors.length - 1))) 1059 | .range(colors); 1060 | 1061 | var c = d3.scale.linear() 1062 | .domain([0, d3.max(datasetBar, function(d){ 1063 | return d.crash_dens; 1064 | })]) 1065 | .range([0,1]); 1066 | 1067 | // update the scales 1068 | xScaleBar.domain([0, d3.max(datasetBar, function(d){ 1069 | return d.crash_dens; 1070 | })]); 1071 | yScaleBar.domain(d3.range(datasetBar.length)); 1072 | 1073 | // update the axes 1074 | var xAxisBar = d3.svg.axis() 1075 | .scale(xScaleBar) 1076 | .orient("bottom") 1077 | .ticks(5) 1078 | .innerTickSize(1) 1079 | .outerTickSize(0); 1080 | 1081 | var yAxisBar = d3.svg.axis() 1082 | .scale(yScaleBar) 1083 | .ticks(0) 1084 | .tickFormat("") 1085 | .orient("right") 1086 | .innerTickSize(1) 1087 | .outerTickSize(0); 1088 | 1089 | // transition in the newby 1090 | svgBar.select(".x.axis") 1091 | .transition() 1092 | .duration(1500) 1093 | .call(xAxisBar); 1094 | 1095 | svgBar.select(".y.axis") 1096 | .transition() 1097 | .duration(1500) 1098 | .call(yAxisBar); 1099 | 1100 | // select the rects and append the data 1101 | var bars = svgBar.selectAll("rect") 1102 | .data(datasetBar); 1103 | 1104 | // add new values at the bottom of the chart 1105 | bars.enter() 1106 | .append("rect") 1107 | .attr("x", margin.left) 1108 | .attr("y", h) 1109 | .attr("width", function(d) { 1110 | return xScaleBar(d.crash_dens); 1111 | }) 1112 | .attr("height", yScaleBar.rangeBand()) 1113 | .attr("fill", function(d, i) { 1114 | return color(i); 1115 | }) 1116 | .attr("opacity", 0); 1117 | 1118 | // update... 1119 | bars.transition() 1120 | .duration(1000) 1121 | .attr("x", margin.left) 1122 | .attr("y", function(d, i) { 1123 | return yScaleBar(i) + margin.top/2; 1124 | }) 1125 | .attr("width", function(d) { 1126 | return xScaleBar(d.crash_dens); 1127 | }) 1128 | .attr("height", yScaleBar.rangeBand()) 1129 | .attr("fill", function(d) { 1130 | return colorRange(c(d.crash_dens)); 1131 | }) 1132 | .attr("opacity", 1); 1133 | 1134 | // exit... 1135 | bars.exit() 1136 | .transition() 1137 | .duration(1000) 1138 | .attr("opacity", 0) 1139 | .remove(); 1140 | 1141 | // select the labels 1142 | var labels = svgBar.selectAll(".chart-label") 1143 | .data(datasetBar); 1144 | 1145 | // get them ready 1146 | labels.enter() 1147 | .append("text") 1148 | .attr("x", margin.left) 1149 | .attr("y", (margin.top + h)) 1150 | .attr("font-family", "HelveticaNeue-Bold, Helvetica, sans-serif") 1151 | .attr("font-size", "8px") 1152 | .attr("fill", "white") 1153 | .attr("class", "chart-label"); 1154 | 1155 | // update 1156 | labels.transition() 1157 | .duration(1000) 1158 | .text(function(d) { 1159 | return d.name + " – " + d.crash_dens; 1160 | }) 1161 | .attr("x", function(d) { 1162 | if (d.crash_dens > 150){ 1163 | return xScaleBar(d.crash_dens) - 30; 1164 | } 1165 | else { 1166 | return margin.left + 15; 1167 | } 1168 | }) 1169 | .attr("y", function(d, i) { 1170 | return yScaleBar(i) + yScaleBar.rangeBand() / 2 + 3 + margin.top/2; 1171 | }); 1172 | 1173 | // exit 1174 | labels.exit() 1175 | .transition() 1176 | .duration(1000) 1177 | .attr("opacity", 0) 1178 | .remove(); 1179 | 1180 | // now just change the title 1181 | svgBar.select("#bar-title") 1182 | .text("Crash Densities in Brooklyn City Council Districts"); 1183 | }, 1184 | manhattan: function(){ 1185 | 1186 | // update the dataset 1187 | var datasetBar = manhattan; 1188 | 1189 | // update the color function 1190 | var colors = ["#8a89a6", "#98abc5", "#a05d56", "#d0743c", "#ff8c00"]; 1191 | 1192 | var colorRange = d3.scale.linear() 1193 | .domain(d3.range(0, 1, 1.0 / (colors.length - 1))) 1194 | .range(colors); 1195 | 1196 | var c = d3.scale.linear() 1197 | .domain([0, d3.max(datasetBar, function(d){ 1198 | return d.crash_dens; 1199 | })]) 1200 | .range([0,1]); 1201 | 1202 | // update the scales 1203 | xScaleBar.domain([0, d3.max(datasetBar, function(d){ 1204 | return d.crash_dens; 1205 | })]); 1206 | yScaleBar.domain(d3.range(datasetBar.length)); 1207 | 1208 | // update the axes 1209 | var xAxisBar = d3.svg.axis() 1210 | .scale(xScaleBar) 1211 | .orient("bottom") 1212 | .ticks(5) 1213 | .innerTickSize(1) 1214 | .outerTickSize(0); 1215 | 1216 | var yAxisBar = d3.svg.axis() 1217 | .scale(yScaleBar) 1218 | .ticks(0) 1219 | .tickFormat("") 1220 | .orient("right") 1221 | .innerTickSize(1) 1222 | .outerTickSize(0); 1223 | 1224 | // transition in the newby 1225 | svgBar.select(".x.axis") 1226 | .transition() 1227 | .duration(1500) 1228 | .call(xAxisBar); 1229 | 1230 | svgBar.select(".y.axis") 1231 | .transition() 1232 | .duration(1500) 1233 | .call(yAxisBar); 1234 | 1235 | // select the rects and append the data 1236 | var bars = svgBar.selectAll("rect") 1237 | .data(datasetBar); 1238 | 1239 | // add new values at the bottom of the chart 1240 | bars.enter() 1241 | .append("rect") 1242 | .attr("x", margin.left) 1243 | .attr("y", h) 1244 | .attr("width", function(d) { 1245 | return xScaleBar(d.crash_dens); 1246 | }) 1247 | .attr("height", yScaleBar.rangeBand()) 1248 | .attr("fill", function(d, i) { 1249 | return color(i); 1250 | }) 1251 | .attr("opacity", 0); 1252 | 1253 | // update... 1254 | bars.transition() 1255 | .duration(1000) 1256 | .attr("x", margin.left) 1257 | .attr("y", function(d, i) { 1258 | return yScaleBar(i) + margin.top/2; 1259 | }) 1260 | .attr("width", function(d) { 1261 | return xScaleBar(d.crash_dens); 1262 | }) 1263 | .attr("height", yScaleBar.rangeBand()) 1264 | .attr("fill", function(d) { 1265 | return colorRange(c(d.crash_dens)); 1266 | }) 1267 | .attr("opacity", 1); 1268 | 1269 | // exit... 1270 | bars.exit() 1271 | .transition() 1272 | .duration(1000) 1273 | .attr("opacity", 0) 1274 | .remove(); 1275 | 1276 | // select the labels 1277 | var labels = svgBar.selectAll(".chart-label") 1278 | .data(datasetBar); 1279 | 1280 | // get them ready 1281 | labels.enter() 1282 | .append("text") 1283 | .attr("x", margin.left) 1284 | .attr("y", (margin.top + h)) 1285 | .attr("font-family", "HelveticaNeue-Bold, Helvetica, sans-serif") 1286 | .attr("font-size", "8px") 1287 | .attr("fill", "white") 1288 | .attr("class", "chart-label"); 1289 | 1290 | // update 1291 | labels.transition() 1292 | .duration(1000) 1293 | .text(function(d) { 1294 | return d.name + " – " + d.crash_dens; 1295 | }) 1296 | .attr("x", function(d) { 1297 | if (d.crash_dens > 350){ 1298 | return xScaleBar(d.crash_dens) - 30; 1299 | } 1300 | else { 1301 | return margin.left + 15; 1302 | } 1303 | }) 1304 | .attr("y", function(d, i) { 1305 | return yScaleBar(i) + yScaleBar.rangeBand() / 2 + 3 + margin.top/2; 1306 | }); 1307 | 1308 | // exit 1309 | labels.exit() 1310 | .transition() 1311 | .duration(1000) 1312 | .attr("opacity", 0) 1313 | .remove(); 1314 | 1315 | // now just change the title 1316 | svgBar.select("#bar-title") 1317 | .text("Crash Densities in Manhattan City Council Districts"); 1318 | }, 1319 | queens: function(){ 1320 | // update the dataset 1321 | var datasetBar = queens; 1322 | 1323 | // update the color function 1324 | var colors = ["#8a89a6", "#98abc5", "#a05d56", "#d0743c", "#ff8c00"]; 1325 | 1326 | var colorRange = d3.scale.linear() 1327 | .domain(d3.range(0, 1, 1.0 / (colors.length - 1))) 1328 | .range(colors); 1329 | 1330 | var c = d3.scale.linear() 1331 | .domain([0, d3.max(datasetBar, function(d){ 1332 | return d.crash_dens; 1333 | })]) 1334 | .range([0,1]); 1335 | 1336 | // update the scales 1337 | xScaleBar.domain([0, d3.max(datasetBar, function(d){ 1338 | return d.crash_dens; 1339 | })]); 1340 | yScaleBar.domain(d3.range(datasetBar.length)); 1341 | 1342 | // update the axes 1343 | var xAxisBar = d3.svg.axis() 1344 | .scale(xScaleBar) 1345 | .orient("bottom") 1346 | .ticks(5) 1347 | .innerTickSize(1) 1348 | .outerTickSize(0); 1349 | 1350 | var yAxisBar = d3.svg.axis() 1351 | .scale(yScaleBar) 1352 | .ticks(0) 1353 | .tickFormat("") 1354 | .orient("right") 1355 | .innerTickSize(1) 1356 | .outerTickSize(0); 1357 | 1358 | // transition in the newby 1359 | svgBar.select(".x.axis") 1360 | .transition() 1361 | .duration(1500) 1362 | .call(xAxisBar); 1363 | 1364 | svgBar.select(".y.axis") 1365 | .transition() 1366 | .duration(1500) 1367 | .call(yAxisBar); 1368 | 1369 | // select the rects and append the data 1370 | var bars = svgBar.selectAll("rect") 1371 | .data(datasetBar); 1372 | 1373 | // add new values at the bottom of the chart 1374 | bars.enter() 1375 | .append("rect") 1376 | .attr("x", margin.left) 1377 | .attr("y", h) 1378 | .attr("width", function(d) { 1379 | return xScaleBar(d.crash_dens); 1380 | }) 1381 | .attr("height", yScaleBar.rangeBand()) 1382 | .attr("fill", function(d, i) { 1383 | return color(i); 1384 | }) 1385 | .attr("opacity", 0); 1386 | 1387 | // update... 1388 | bars.transition() 1389 | .duration(1000) 1390 | .attr("x", margin.left) 1391 | .attr("y", function(d, i) { 1392 | return yScaleBar(i) + margin.top/2; 1393 | }) 1394 | .attr("width", function(d) { 1395 | return xScaleBar(d.crash_dens); 1396 | }) 1397 | .attr("height", yScaleBar.rangeBand()) 1398 | .attr("fill", function(d) { 1399 | return colorRange(c(d.crash_dens)); 1400 | }) 1401 | .attr("opacity", 1); 1402 | 1403 | // exit... 1404 | bars.exit() 1405 | .transition() 1406 | .duration(1000) 1407 | .attr("opacity", 0) 1408 | .remove(); 1409 | 1410 | // select the labels 1411 | var labels = svgBar.selectAll(".chart-label") 1412 | .data(datasetBar); 1413 | 1414 | // get them ready 1415 | labels.enter() 1416 | .append("text") 1417 | .attr("x", margin.left) 1418 | .attr("y", (margin.top + h)) 1419 | .attr("font-family", "HelveticaNeue-Bold, Helvetica, sans-serif") 1420 | .attr("font-size", "8px") 1421 | .attr("fill", "white") 1422 | .attr("class", "chart-label"); 1423 | 1424 | // update 1425 | labels.transition() 1426 | .duration(1000) 1427 | .text(function(d) { 1428 | return d.name + " – " + d.crash_dens; 1429 | }) 1430 | .attr("x", function(d) { 1431 | if (d.crash_dens > 125){ 1432 | return xScaleBar(d.crash_dens) - 30; 1433 | } 1434 | else { 1435 | return margin.left + 15; 1436 | } 1437 | }) 1438 | .attr("y", function(d, i) { 1439 | return yScaleBar(i) + yScaleBar.rangeBand() / 2 + 3 + margin.top/2; 1440 | }); 1441 | 1442 | // exit 1443 | labels.exit() 1444 | .transition() 1445 | .duration(1000) 1446 | .attr("opacity", 0) 1447 | .remove(); 1448 | 1449 | // now just change the title 1450 | svgBar.select("#bar-title") 1451 | .text("Crash Densities in Queens City Council Districts"); 1452 | }, 1453 | statenisland: function(){ 1454 | 1455 | // update the dataset 1456 | var datasetBar = staten; 1457 | 1458 | // update the color function 1459 | var colors = ["#8a89a6", "#98abc5", "#a05d56", "#d0743c", "#ff8c00"]; 1460 | 1461 | var colorRange = d3.scale.linear() 1462 | .domain(d3.range(0, 1, 1.0 / (colors.length - 1))) 1463 | .range(colors); 1464 | 1465 | var c = d3.scale.linear() 1466 | .domain([0, d3.max(datasetBar, function(d){ 1467 | return d.crash_dens; 1468 | })]) 1469 | .range([0,1]); 1470 | 1471 | // update the scales 1472 | xScaleBar.domain([0, d3.max(datasetBar, function(d){ 1473 | return d.crash_dens; 1474 | })]); 1475 | yScaleBar.domain(d3.range(datasetBar.length)); 1476 | 1477 | // update the axes 1478 | var xAxisBar = d3.svg.axis() 1479 | .scale(xScaleBar) 1480 | .orient("bottom") 1481 | .ticks(5) 1482 | .innerTickSize(1) 1483 | .outerTickSize(0); 1484 | 1485 | var yAxisBar = d3.svg.axis() 1486 | .scale(yScaleBar) 1487 | .ticks(0) 1488 | .tickFormat("") 1489 | .orient("right") 1490 | .innerTickSize(1) 1491 | .outerTickSize(0); 1492 | 1493 | // transition in the newby 1494 | svgBar.select(".x.axis") 1495 | .transition() 1496 | .duration(1500) 1497 | .call(xAxisBar); 1498 | 1499 | svgBar.select(".y.axis") 1500 | .transition() 1501 | .duration(1500) 1502 | .call(yAxisBar); 1503 | 1504 | // select the rects and append the data 1505 | var bars = svgBar.selectAll("rect") 1506 | .data(datasetBar); 1507 | 1508 | // add new values at the bottom of the chart 1509 | bars.enter() 1510 | .append("rect") 1511 | .attr("x", margin.left) 1512 | .attr("y", (margin.top + h)) 1513 | .attr("width", function(d) { 1514 | return xScaleBar(d.crash_dens); 1515 | }) 1516 | .attr("height", yScaleBar.rangeBand()) 1517 | .attr("fill", function(d, i) { 1518 | return color(i); 1519 | }) 1520 | .attr("opacity", 0); 1521 | 1522 | // update... 1523 | bars.transition() 1524 | .duration(1000) 1525 | .attr("x", margin.left) 1526 | .attr("y", function(d, i) { 1527 | return yScaleBar(i) + margin.top/2; 1528 | }) 1529 | .attr("width", function(d) { 1530 | return xScaleBar(d.crash_dens); 1531 | }) 1532 | .attr("height", yScaleBar.rangeBand()) 1533 | .attr("fill", function(d) { 1534 | return colorRange(c(d.crash_dens)); 1535 | }) 1536 | .attr("opacity", 1); 1537 | 1538 | // exit... 1539 | bars.exit() 1540 | .transition() 1541 | .duration(1000) 1542 | .attr("opacity", 0) 1543 | .remove(); 1544 | 1545 | // select the labels 1546 | var labels = svgBar.selectAll(".chart-label") 1547 | .data(datasetBar); 1548 | 1549 | // get them ready 1550 | labels.enter() 1551 | .append("text") 1552 | .attr("x", margin.left) 1553 | .attr("y", (margin.top + h)) 1554 | .attr("font-family", "HelveticaNeue-Bold, Helvetica, sans-serif") 1555 | .attr("font-size", "8px") 1556 | .attr("fill", "white") 1557 | .attr("class", "chart-label"); 1558 | 1559 | // update 1560 | labels.transition() 1561 | .duration(1000) 1562 | .text(function(d) { 1563 | return d.name + " – " + d.crash_dens; 1564 | }) 1565 | .attr("x", function(d) { 1566 | if (d.crash_dens > 10){ 1567 | return xScaleBar(d.crash_dens) - 30; 1568 | } 1569 | else { 1570 | return margin.left + 15; 1571 | } 1572 | }) 1573 | .attr("y", function(d, i) { 1574 | return yScaleBar(i) + yScaleBar.rangeBand() / 2 + 3 + margin.top/2; 1575 | }); 1576 | 1577 | // exit 1578 | labels.exit() 1579 | .transition() 1580 | .duration(1000) 1581 | .attr("opacity", 0) 1582 | .remove(); 1583 | 1584 | // now just change the title 1585 | svgBar.select("#bar-title") 1586 | .text("Crash Densities in Staten Island City Council Districts"); 1587 | }, 1588 | reset: function(){ 1589 | 1590 | // update the dataset 1591 | var datasetBar = boroughs; 1592 | 1593 | // update the color function 1594 | var color = d3.scale.ordinal() 1595 | .range(["#98abc5", "#d0743c", "#ff8c00", "#a05d56", "#8a89a6"]); 1596 | 1597 | // update the scales 1598 | xScaleBar.domain([0, d3.max(datasetBar, function(d){ 1599 | return d.crash_dens; 1600 | })]); 1601 | yScaleBar.domain(d3.range(datasetBar.length)); 1602 | 1603 | // update the axes 1604 | var xAxisBar = d3.svg.axis() 1605 | .scale(xScaleBar) 1606 | .orient("bottom") 1607 | .ticks(5) 1608 | .innerTickSize(1) 1609 | .outerTickSize(0); 1610 | 1611 | var yAxisBar = d3.svg.axis() 1612 | .scale(yScaleBar) 1613 | .ticks(0) 1614 | .tickFormat("") 1615 | .orient("right") 1616 | .innerTickSize(1) 1617 | .outerTickSize(0); 1618 | 1619 | // transition in the newby 1620 | svgBar.select(".x.axis") 1621 | .transition() 1622 | .duration(1500) 1623 | .call(xAxisBar); 1624 | 1625 | svgBar.select(".y.axis") 1626 | .transition() 1627 | .duration(1500) 1628 | .call(yAxisBar); 1629 | 1630 | // select the rects and append the data 1631 | var bars = svgBar.selectAll("rect") 1632 | .data(datasetBar); 1633 | 1634 | // add new values at the bottom of the chart 1635 | bars.enter() 1636 | .append("rect") 1637 | .attr("x", margin.left) 1638 | .attr("y", (margin.top + h)) 1639 | .attr("width", function(d) { 1640 | return xScaleBar(d.crash_dens); 1641 | }) 1642 | .attr("height", yScaleBar.rangeBand()) 1643 | .attr("fill", function(d, i) { 1644 | return color(i); 1645 | }) 1646 | .attr("opacity", 0); 1647 | 1648 | // update... 1649 | bars.transition() 1650 | .duration(1000) 1651 | .attr("x", margin.left) 1652 | .attr("y", function(d, i) { 1653 | return yScaleBar(i) + margin.top/2; 1654 | }) 1655 | .attr("width", function(d) { 1656 | return xScaleBar(d.crash_dens); 1657 | }) 1658 | .attr("height", yScaleBar.rangeBand()) 1659 | .attr("fill", function(d, i) { 1660 | return color(i); 1661 | }) 1662 | .attr("opacity", 1);; 1663 | 1664 | // exit... 1665 | bars.exit() 1666 | .transition() 1667 | .duration(1000) 1668 | .attr("opacity", 0) 1669 | .remove(); 1670 | 1671 | // select the labels 1672 | var labels = svgBar.selectAll(".chart-label") 1673 | .data(datasetBar); 1674 | 1675 | // get them ready 1676 | labels.enter() 1677 | .append("text") 1678 | .attr("x", margin.left) 1679 | .attr("y", (margin.top + h)) 1680 | .attr("font-family", "HelveticaNeue-Bold, Helvetica, sans-serif") 1681 | .attr("font-size", "10px") 1682 | .attr("fill", "white") 1683 | .attr("class", "chart-label"); 1684 | 1685 | // update 1686 | labels.transition() 1687 | .duration(1000) 1688 | .text(function(d) { 1689 | return d.name + " – " + d.crash_dens; 1690 | }) 1691 | .attr("x", function(d) { 1692 | if (d.crash_dens > 200){ 1693 | return xScaleBar(d.crash_dens) - 40; 1694 | } 1695 | else { 1696 | return margin.left + 15; 1697 | } 1698 | }) 1699 | .attr("y", function(d, i) { 1700 | return yScaleBar(i) + yScaleBar.rangeBand() / 2 + 3 + margin.top/2; 1701 | }); 1702 | 1703 | // exit 1704 | labels.exit() 1705 | .transition() 1706 | .duration(1000) 1707 | .attr("opacity", 0) 1708 | .remove(); 1709 | 1710 | // now just change the title 1711 | svgBar.select("#bar-title") 1712 | .text("Distribution of Crash Densities by Borough"); 1713 | } 1714 | } 1715 | 1716 | // transition bars on zoom 1717 | $('#zoom_selector').change(function(){ 1718 | D3BarActions[$(this).val()](); 1719 | }); 1720 | 1721 | } 1722 | -------------------------------------------------------------------------------- /Javascripts/manhattan_crash_proportions.js: -------------------------------------------------------------------------------- 1 | var manhattan = [ 2 | { 3 | "name": "Dis. 1", 4 | "crash_dens": 599.1 5 | }, 6 | { 7 | "name": "Dis. 2", 8 | "crash_dens": 933.2 9 | }, 10 | { 11 | "name": "Dis. 3", 12 | "crash_dens": 630.7 13 | }, 14 | { 15 | "name": "Dis. 4", 16 | "crash_dens": 798.7 17 | }, 18 | { 19 | "name": "Dis. 5", 20 | "crash_dens": 553 21 | }, 22 | { 23 | "name": "Dis. 6", 24 | "crash_dens": 208.4 25 | }, 26 | { 27 | "name": "Dis. 7", 28 | "crash_dens": 324.8 29 | }, 30 | { 31 | "name": "Dis. 8", 32 | "crash_dens": 301.3 33 | }, 34 | { 35 | "name": "Dis. 9", 36 | "crash_dens": 469.4 37 | }, 38 | { 39 | "name": "Dis. 10", 40 | "crash_dens": 255.1 41 | } 42 | ]; -------------------------------------------------------------------------------- /Javascripts/queens_crash_proportions.js: -------------------------------------------------------------------------------- 1 | var queens = [ 2 | { 3 | "name": "Dis. 19", 4 | "crash_dens": 40.6 5 | }, 6 | { 7 | "name": "Dis. 20", 8 | "crash_dens": 176 9 | }, 10 | { 11 | "name": "Dis. 21", 12 | "crash_dens": 167.4 13 | }, 14 | { 15 | "name": "Dis. 22", 16 | "crash_dens": 128 17 | }, 18 | { 19 | "name": "Dis. 23", 20 | "crash_dens": 33 21 | }, 22 | { 23 | "name": "Dis. 24", 24 | "crash_dens": 86.4 25 | }, 26 | { 27 | "name": "Dis. 25", 28 | "crash_dens": 339.9 29 | }, 30 | { 31 | "name": "Dis. 26", 32 | "crash_dens": 210.6 33 | }, 34 | { 35 | "name": "Dis. 27", 36 | "crash_dens": 113.4 37 | }, 38 | { 39 | "name": "Dis. 28", 40 | "crash_dens": 93.4 41 | }, 42 | { 43 | "name": "Dis. 29", 44 | "crash_dens": 159.1 45 | }, 46 | { 47 | "name": "Dis. 30", 48 | "crash_dens": 67.6 49 | }, 50 | { 51 | "name": "Dis. 31", 52 | "crash_dens": 36.6 53 | }, 54 | { 55 | "name": "Dis. 32", 56 | "crash_dens": 54.5 57 | } 58 | ]; -------------------------------------------------------------------------------- /Javascripts/staten_island_crash_proportions.js: -------------------------------------------------------------------------------- 1 | var staten = [ 2 | { 3 | "name": "Dis. 49", 4 | "crash_dens": 54 5 | }, 6 | { 7 | "name": "Dis. 50", 8 | "crash_dens": 20.3 9 | }, 10 | { 11 | "name": "Dis. 51", 12 | "crash_dens": 10 13 | } 14 | ]; -------------------------------------------------------------------------------- /Logos/Azavea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summer-of-maps/2016-TransAlt-TrafficCrashVisualization/684324fd1b6422096250b0d51d4332c07e94da8d/Logos/Azavea.png -------------------------------------------------------------------------------- /Logos/TransAlt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summer-of-maps/2016-TransAlt-TrafficCrashVisualization/684324fd1b6422096250b0d51d4332c07e94da8d/Logos/TransAlt.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Road to Vision Zero: Traffic Crashes and Poverty in New York City 2 | **An interactive web application built on CARTO and D3 for visualizing relationships between traffic crashes and poverty in NYC. Click [here](https://parkerziegler.github.io/transalt-visionzero-app/) to see it in action.** 3 | 4 | *This repo is a result of data analysis completed as part of the 2016 Summer of Maps Program. This work was completed in support of a pro bono project with [Transportation Alternatives](https://www.transalt.org/).* 5 | 6 | *[Summer of Maps](http://www.summerofmaps.com/) is a fellowship program organized and facilitated by [Azavea](https://www.azavea.com/). Azavea is a B Corporation that specializes in civic-minded GIS software development and spatial analysis.* 7 | *Summer of Maps offers stipends to student spatial analysts to perform data analysis and visualization for non-profit organizations. Every year we match up non-profit organizations that have spatial analysis needs with talented students to implement projects over a three-month period during the summer.* 8 | 9 | ### Purpose of the Work 10 | [Transportation Alternatives](https://www.transalt.org/) expressed interest in developing an interactive web application that would allow users to explore suspected relationships between traffic crashes and poverty in New York City. To do this, the Summer of Maps Fellow for this project, Parker Ziegler, decided to integrate *geospatial visualization* techniques using the [CARTO javascript library](https://carto.com/docs/carto-engine/carto-js/) with *statistical visualization* techniques using the [D3 javascript library](https://d3js.org/). 11 | 12 | Dropdown menus are used as the central control point for the visualization. Using [jQuery](https://jquery.com/), layer changes and D3 transitions are wired to the dropdowns in the dashboard, allowing for data views that respond dynamically to user input and hide extraneous information. [Leaflet](http://leafletjs.com/) is also used in the visualization to generate tooltips. 13 | 14 | The ultimate design vision for this project was to create an experience that was intuitive and engaging for both short-term (30 second) and long-term (10-15 minute) users, and that was sensitive to the different ways that people intuit information. 15 | 16 | ### Intended Use for this Work 17 | This web application is intended for use by New York City community organizations, businesses, political bodies, and the general public. The hope is that this application can allow interested users to explore data on traffic crashes and poverty in their own city council districts or boroughs. We hope, as well, that it motivates people to take action. 18 | 19 | The code for this application is open and free to be forked, modified, or repurposed. 20 | 21 | ### Acknowledgements 22 | [Parker Ziegler](http://parkerziegler.com/) was the lead developer and designer for this project. Special thanks to Daniel McGlone, Senior GIS Analyst at Azavea, for his mentorship on this project. 23 | 24 | Thanks are also due to [Mike Bostock](https://bost.ocks.org/mike/), the creator of D3, and [Scott Murray](http://alignedleft.com/tutorials) for his extremely helpful D3 tutorials. 25 | 26 | Crash data for this project came courtesy of Transportation Alternatives. Socioeconmic and demographic data came courtesy of the U.S. Census Bureau's American Community Survey. Shapefiles for NYC administrative boundaries came from NYC Open Data. 27 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | The Road to Vision Zero: Traffic Crashes and Poverty in New York City 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 41 | 42 | 43 | 44 | 78 | 79 | 80 | 112 | 113 | 114 | 156 | 157 | 189 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 270 | 271 | 302 | 303 | 334 | 335 | 366 | 367 | 368 |
369 |
370 |
371 |
EXPLORE DATA ON NYC
CITY COUNCIL DISTRICTS
372 |
373 |
374 |
375 |
376 |
377 | 378 | 386 |
387 |
388 | 389 | 397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |

THE ROAD TO VISION ZERO

406 |
407 |
408 |

TRAFFIC CRASHES AND POVERTY IN NEW YORK CITY

409 |
410 |
411 |

ABOUT 412 |

413 |
414 |

The Road to Vision Zero: Traffic Crashes and Poverty in New York City is an interactive web application designed to help users visualize spatial and statistical relationships between traffic crashes and poverty in NYC. Use the dropdown menus to view different variables across the city or zoom to a specific borough. As you do, notice the scatterplot change to reflect the relationship between the selected variable and the crash density and notice the bar chart change to display crash densities of specific city council districts.

Data for this project comes from Transportation Alternatives, NYC Open Data, and the U.S. Census Bureau's American Community Survey. Development and design by Parker Ziegler, Azavea.

415 |
416 |
417 | 418 | 419 | 420 | 421 | 422 | 423 |
424 |
425 |

Median Income ($)

426 |
427 |
428 |

$120,000

429 |
430 |
431 |

$20,000

432 |
433 | 434 | The Road to Vision Zero: Traffic Crashes and Poverty in New York City 435 | --------------------------------------------------------------------------------