Skip to content

Instantly share code, notes, and snippets.

@clhenrick
Last active April 7, 2016 13:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save clhenrick/799e89c53956376194de to your computer and use it in GitHub Desktop.
Save clhenrick/799e89c53956376194de to your computer and use it in GitHub Desktop.
Integrating Cartodb.js layer toggling with Odyssey.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NWB map intro test</title>
<meta name="description" content="An introduction to the Bushwick Community Map">
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<link rel="stylesheet" href="http://cartodb.github.io/odyssey.js/sandbox/css/slides.css">
<link href='https://api.tiles.mapbox.com/mapbox.js/v2.1.4/mapbox.css' rel='stylesheet'/>
<style>
body {
text-align: left;
margin: 0; padding: 0;
font-family: Georgia;
background: #EEE;
}
h2 {
font-weight: normal;
}
#slides_container .slide {
max-height: 600px;
}
</style>
</head>
<body>
<div id="map" style="width: 100%; height: 100%"></div>
<div id="slides_container" style="display:block;">
<div id="dots"></div>
<div id="slides">
<div class="slide">
<h1 id="rheingold">The Rheingold Development</h1>
<p>
The North West Bushwick Community Group began in reaction to the Rheingold Rezoning in Bushwick in September, 2013. New and old residents came together to form a group that would advocate against the displacement of Bushwick's longtime working-class and low-income residents and for accountability from developers.
</p>
<p>
“In the 1840s and 1850s, a majority of the [Bushwick] immigrants were German, which became the dominant population. Bushwick established a considerable brewery industry, including "Brewer's Row"—14 breweries operating in a 14-block area—by 1890. Thus, Bushwick was dubbed the "beer capital of the Northeast". The last Bushwick brewery closed its doors in 1976.”<br>
<a href="http://en.m.wikipedia.org/wiki/Bushwick,_Brooklyn">-wikipedia</a>
</p>
<p>
Rheingold brewery closed in 1976. This industrial land was (potentially willingly) underutilized for years serving as loading docks and truck storage, as well as serving less formal industrial and commercial uses. The Mademoiselle building (Flushing &amp; Evergreen) was built with public subsidies serving as a garment factory and local employer.
</p>
<p>
In 2010 Read Properties (owner &amp; developer of the Rheingold site including the Mademoiselle building) filed a ULURP application with NYC City Planning Department. Through this ULURP process the landowner was asking for the Communities’ permission to change the zoning of this land and allow Read to build residential on industrial land. This zoning change would radically alter the community and provide enormous profit for the developer.
</p>
<p>
On September 3, 2013 Bushwick Residents were invited by CM Reyna to attend a Town Hall meeting to inform the community about the ULURP (rezoning) application and proposed Residential Development at Rheingold. The attending Community response was clear and direct in it's articulation of local need and concern around the Read Properties proposal. Neighbors convened after the Town Hall and again voiced communal concern. A subsequent step in the Rheingold Rezoning ULURP process- a Planning Commission Public Hearing galvanized neighbors around calling for equitable land use in Bushwick. Most neighbors in attendance at these public events were unaware of the ULURP Rezoning process already underway at the Rheingold site.
</p>
<p>
In December 2013 The New York City Council approved the rezoning of Rheingold and certified Read Properties’ ULURP application. The 9 blocks of formerly industrial land is now zoned for residential use with nearly 900 luxury apartments slated to built here over the next few years. This massive residential development will impact Bushwick’s long standing working-class &amp; low-income communities and potentially serves to threaten local affordable housing stability.
</p>
<p>
The mademoiselle building (Flushing &amp; Evergreen) now serves as Bushwick’s largest remaining M1 (industrially zoned) plot of land and is occupied now by the city agency- Office of Emergency Management. In addition to light Manufacture uses M1 zoning allows for hotels &amp; nightclubs. This M1 plot is vulnerable to developers who see more profit in land-uses that do not serve Bushwick’s long standing communities or local need.
</p>
</div>
<div class="slide">
<h1 id="far">F.A.R. map layer</h1>
<p>
As you look at the former Rheingold site on the Bushwick Community Map:
</p>
<p>
Using the FAR layer you see a high percentage of available FAR at the Rheingold site (shown through darker shading). Available FAR potentially indicates a land owner/developer/landlord may be incentivized to redevelop a property. This can mean building new buildings. tearing down older buildings and replacing with new (often more expensive) buildings, or even adding more floors on top of existing structures.
</p>
<p>
Available FAR (darker shading) means the city zoning code allows a landowner/developer to build a bigger buildings, or more units, and potentially make more profit. Often when there is available FAR and money to be made, landowners choose to make money before prioritizing community need or tenants’ interests.
</p>
<p>
Available FAR is a potential indication a plot of land or a building is vulnerable to redevelopment or unwelcome market-forces.
</p>
</div>
<div class="slide">
<h1>New Buildings map layer</h1>
<p>
The Rheingold site is surrounded by newly constructed buildings (blue dots on the map indicate newly built buildings, these dots surround the Rheingold site). The North West section of CD4/Bushwick has recently experienced a major shift in its built & human environments- lots of new residents and lots of newer, more expensive, apartment buildings. The proximity of the Rheingold site to New Buildings (often luxury buildings) may potentially indicate the Rheingold site (which also has available FAR) is vulnerable to (unwelcome) market rate/luxury redevelopment.
</p>
<p>
Vacant Land or property with a high rate of available FAR which is near market or luxury New Buildings may be a future Site of gentrification...
</p>
</div>
<div class="slide">
<h1>Likely Rent Stabilized map layer</h1>
<p>
The Rheingold site is surrounded by a number of Likely Rent Stabilized buildings. Both the tenants living in these rent-stabilized buildings and the rent-stabilized/affordable units themselves are potentially vulnerable to unwelcome changes in the community and changes in their housing stability. The proximity of these rent-stabilized buildings to a luxury development site potentially indicates the need for a targeted outreach campaign to the tenants in these rent-stabilized buildings. Potentially research and outreach could be done to ensure these rent-controlled community members know their rights are are able to stay in their homes as they want to.
</p>
<p>
As the local market rates rise in Bushwick with influxes of luxury developments, owners of rent-stabilized buildings may be willing to raise the prices of their affordable units, harass or even evict tenants in order to de-regulate the units or rent to higher paying tenants.
</p>
<p>
Tenants subjected to harassment, neglect or pressure from their landlord can check out the Get Help links for resources and support to help understand and fight for housing rights.
</p>
</div>
<div class="slide">
<h1 id="colony"> Colony 1209 </h1>
<h2>Property History</h2>
<p>
The year was 2006 and Bushwick’s gentrification was still nascent, although the enthusiasm for building condos in Williamsburg showed no sign of stopping the march eastward. A large, former mattress factory located at 1209 Dekalb Avenue was purchased by the Bushwick Enterprise Group for $8.3 million, with intentions to demolish the industrial building. The project stalled when one the investors, Eli Weistein, was convicted of fraud and the site eventualy went into foreclosure to be sold at auction.
</p>
<p>
In 2012, the site was acquired by Read Property Group for $6 million, who operated under a LLC named ‘1209 Dekalb Holdings LLC’. They prompted resumed the luxury development process and completed the construction of the building known as “COLONY 1209.” The 127-unit building contains NO affordable units, where a studio starts at $1875/month and includes numerous luxury amenities such as a gym and a 24-hour doorman.
</p>
<p>
As with the Reingold rezoning, Bushwick’s former industrial land is being transformed into luxury developments. These buildings provide no benefits for the local residents and cause rents to increase, displace the working-class tenants of Bushwick. Rents in Bushwick have increased 23% this year.
</p>
</div>
<div class="slide">
<h1>Colony 1209</h1>
<h2>“Gentrification is the New Colonialism”</h2>
<p>
The building itself caused outrage in the community. It uses colonist language in it’s website and advertising, declaring Bushwick to be a ‘new frontier’. It invites the young and rich to become ‘settlers’ and ‘pioneers’.
</p>
<p>
As the building became to be rented, a neighborhood group, [Reclaim Bushwick](https://www.facebook.com/pages/Reclaim-Bushwick/664471223623389), launched to target the building. They compare gentrification to colonialism and point out the offensiveness of 1209 Dekalb, often referred to simply as “The Colony”. Their flyer explains their opposition to this breed of real estate.
</p>
</div>
<div class="slide">
<h1>Colony 1209</h1>
<h2>Profit, Profit and More Profit</h2>
<p>
In 2012, Read Property Group, sold Colony 1209 for $58 million dollars to Spruce Capital. In under 10 years, the site went from a vacant factory to a luxury rental building. The 127-units were all built within the existing zoning and therefore did not require any special permission to construct. The community had no say in the process.
</p>
<p>
Only 7 months after purchasing the property, Spruce Capital wants to sell the building for $82 million.
</p>
<p>
Despite containing no affordable units, the property still benefits from tax subsidies: it is in it’s 3rd year of a 15 year tax abatement from the 421a program.
</p>
</div>
<div class="slide">
<h1 id="linden"> 98 Linden</h1>
<img src="../images/98linden.jpg">
<p>
Tenants at 98 Linden Street were asked to grant access to their apartment at the request of their landlord, Joel Israel, under the auspices of minor repairs and maintenance. Instead, their kitchens and bathrooms were destroyed by the landlord in an (alleged) attempt to make the homes unlivable and force these rent controlled tenants out of their apartments, where they lived for 23 years. A vacant building can be easily taken out of rent regulation and converted to market rate units providing a windfall profit for landlords willing/able to vacate low paying renters. Preserving affordable units, and keeping people in their homes, are important to the housing stability of local long standing communities.
</p>
<p>
The ACRIS info (public tax & ownership info) lists the owner of 98 Linden as LINDEN VENTURES LLC.
</p>
</div>
<div class="slide">
<h1>DOB Permits map layer</h1>
<p>
Using the DOB Permits Map Layer reveals 98 Linden Street has an active DOB permit. Also revealed is the property owners name/applicant name- and a phone number- Joel Israel, (212) 203-6230. ACRIS and Tax info don’t always reveal the property owner's name. Most often an LLC is listed as the property owner in ACRIS/tax documents. This corporate name allows landlords to remain anonymous while collecting rent behind a wall of legal structures. These various corporate names and layers of business partners make it difficult for tenants to know who they are paying rent to and who is responsible for the local built environment. Potentially one owner (or corporate developer) controls a number of properties & LLCs under different names. Currently there is no way to parse public data to determine the true ownership of our community.
</p>
</div>
<div class="slide">
<h1>End of Tour</h1>
<p>
Thanks for exploring the stories and map layers. Now feel free to go explore the map layers on your own!
</p>
</div>
</div>
<ul id="navButtons">
<li><a class="prev"></a></li>
<li><a class="next"></a></li>
</ul>
</div>
<!-- <div id="credits">
<span class="title" id="title">Bushwick Community Map Introduction</span>
<span class="author"><strong id="author">By @clhenrick using</strong> <a href="http://cartodb.github.io/odyssey.js/">Odyssey.js</a><span></span></span>
</div> -->
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://cartodb-libs.global.ssl.fastly.net/cartodb.js/v3/cartodb.js"></script>
<script src='https://api.tiles.mapbox.com/mapbox.js/v2.1.4/mapbox.standalone.js'></script>
<script src="http://cartodb.github.io/odyssey.js/dist/odyssey.js" charset="UTF-8"></script>
<script>
var app = app || {};
app.odysseyTest = (function(w,d,$,O) {
var el = {
map : null,
tileLayer : null,
story : null,
bushwick : null,
rheingold : null,
colony : null,
linden : null,
featureGroup : null,
rheingoldJson : null,
taxLots: null,
dobPermitsNB : null,
dobPermitsA1: null,
dobPermitsA2A3: null
};
var sql = {
all : "SELECT * FROM bushwick_pluto14v1",
rentStab : "SELECT a.* FROM bushwick_pluto14v1 a, bushwick_rent_stabl_merge_centroids b where st_intersects(a.the_geom, b.the_geom)",
vacant : "SELECT * FROM bushwick_pluto14v1 WHERE landuse = '11'",
};
var mapStyles = {
regular : '#bushwick_pluto14v1 {' +
'polygon-fill: hsl(200,40%,90%);' +
'polygon-opacity: 0.75;' +
'line-color: #000;' +
'line-width: 0.2;' +
'line-opacity: 0.5;' +
'}',
// red highlight
red : '#bushwick_pluto14v1 {' +
'polygon-fill: hsl(0,100%,30%);' +
'polygon-opacity: 0.75;' +
'line-color: #000;' +
'line-width: 0.2;' +
'line-opacity: 0.5;' +
'}',
// choropleth style for available FAR
availFAR : "#bushwick_pluto14v1{" +
"polygon-fill: #FFFFB2;" +
"polygon-opacity: 0.8;" +
"line-color: #000;" +
"line-width: 0.2;" +
"line-opacity: 0.5;" +
"}" +
"#bushwick_pluto14v1 [ availablefar <= 4] {" +
"polygon-fill: #BD0026;" +
"}" +
"#bushwick_pluto14v1 [ availablefar <= 3.2] {" +
"polygon-fill: #F03B20;" +
"}" +
"#bushwick_pluto14v1 [ availablefar <= 2.4000000000000004] {" +
"polygon-fill: #FD8D3C;" +
"}" +
"#bushwick_pluto14v1 [ availablefar <= 1.6] {" +
"polygon-fill: #FECC5C;" +
"}" +
"#bushwick_pluto14v1 [ availablefar <= 0.8] {" +
"polygon-fill: #FFFFB2;" +
"}"
};
var initMap = function() {
L.mapbox.accessToken = 'pk.eyJ1IjoiY2hlbnJpY2siLCJhIjoiLVhZMUZZZyJ9.HcNi26J3P-MiOmBKYHIbxw';
el.map = new L.map('map').setView([40.6941, -73.9162], 16);
el.tileLayer = L.mapbox.tileLayer('chenrick.map-3gzk4pem').addTo(el.map);
el.bushwick = new L.LatLng(40.6941, -73.9162);
el.rheingold = new L.LatLng(40.700740, -73.934209);
el.colony = new L.LatLng(40.695867,-73.928153);
el.linden = new L.LatLng(40.692776,-73.919756);
el.featureGroup = L.featureGroup().addTo(el.map);
loadRheingold();
}
var loadCdbData = function() {
var cdbURL = "http://bushwick.cartodb.com/api/v2/viz/64ceb582-71e2-11e4-b052-0e018d66dc29/viz.json";
cartodb.createLayer(el.map, cdbURL, {
cartodb_logo: false,
legends: false
},
function(layer) {
el.taxLots = layer.getSubLayer(0);
el.dobPermitsNB = layer.createSubLayer({
sql : "SELECT * FROM exp_codedjobs_nb",
cartocss : '#exp_codedjobs_nb {marker-width: 10; marker-fill: hsl(350,0%,0%); marker-line-color: white; marker-line-width: 0.8;}'
});
el.dobPermitsA1 = layer.createSubLayer({
sql : "SELECT * FROM exp_codedjobs_a1",
cartocss : '#exp_codedjobs_a1 {marker-width: 10; marker-fill: hsl(0,0%,30%); marker-line-color: white; marker-line-width: 0.8;}'
});
el.dobPermitsA2A3 = layer.createSubLayer({
sql : "SELECT * FROM exp_codedjobs_a2a3",
cartocss : '#exp_codedjobs_a2a3 {marker-width: 10; marker-fill: hsl(0,0%,30%); marker-line-color: white; marker-line-width: 0.8;}'
});
console.log('dob sublayers: ', el.dobPermitsA1, el.dobPermitsA2A3 );
// hide layers on load
el.taxLots.hide();
el.dobPermitsNB.hide();
el.dobPermitsA1.hide();
el.dobPermitsA2A3.hide();
el.map.addLayer(layer, false);
el.tileLayer.bringToBack();
}).on('done', function(){
// console.log('taxLots: ', el.taxLots);
el.featureGroup.addLayer(el.rheingoldJson);
el.map.fitBounds(el.rheingoldJson);
});
}
function click(el) {
var element = O.Core.getElement(el);
var t = O.Trigger();
function click() {
// console.log(el);
t.trigger();
}
if (element) element.onclick = click;
return t;
}
// load the geoJSON boundary for the Rheingold development
function loadRheingold() {
$.getJSON('./rheingold_pluto_dissolved.geojson', function(json, textStatus) {
el.rheingoldJson = L.geoJson(json, {
style: { color: '#000', fill: false, dashArray: '5,10', lineCap: 'square', clickable: false }
});
});
}
// trigger a custom event called SlideChange
var emitSlideChange = O.Action( function() {
$(document).trigger('slideChange', function() {
console.log(seq.current())
});
});
// listen for the slideChange event to be triggered
function listenSlideChange() {
$(document).on('slideChange', function() {
console.log('listened to slidechange');
trackCurrentSlide();
});
}
// fire this each time the user changes a slide
function trackCurrentSlide() {
slides = $('#slides').children(); // creates an array of slides
slides.each(function(i){
if ($(this).hasClass('selected')) {
console.log('index: ', i);
checkIndex(i);
}
});
}
// check the index being returned by trackCurrentSlide()
function checkIndex(index) {
switch(index){
case 0: console.log('first slide!'), slideOne();
break;
case 1: console.log('second slide!'), slideTwo();
break;
case 2: console.log('third slide!'), slideThree();
break;
case 3: console.log('fourth slide'), slideFour();
break;
case 4: console.log('fifth slide'), slideFive();
break;
case 5: console.log('sixth slide');
break;
case 6: console.log('seventh slide');
break;
case 7: console.log('eigth slide'), slideEight();
break;
case 8: console.log('nineth slide'), slideNine();
break;
case 9: console.log('tenth slide'), slideTen();
break;
default: console.log('out of slide counters');
}
}
function slideOne() {
if (!el.featureGroup.hasLayer(el.rheingoldJson)) {
el.featureGroup.addLayer(el.rheingoldJson);
}
el.map.fitBounds(el.rheingoldJson);
el.taxLots.hide();
el.dobPermitsA1.hide();
el.dobPermitsA2A3.hide();
}
function slideTwo() {
el.taxLots.setSQL(sql.all)
el.taxLots.setCartoCSS(mapStyles.availFAR)
el.taxLots.show();
}
function slideThree() {
el.taxLots.hide();
el.dobPermitsNB.show();
}
function slideFour() {
el.dobPermitsNB.hide();
el.taxLots.setSQL(sql.rentStab);
el.taxLots.setCartoCSS(mapStyles.red);
el.taxLots.show();
}
function slideFive() {
el.taxLots.hide();
if (el.featureGroup.hasLayer(el.rheingoldJson)) {
el.featureGroup.removeLayer(el.rheingoldJson);
}
}
function slideEight() {
el.dobPermitsA1.hide();
el.dobPermitsA2A3.hide();
}
function slideNine() {
el.dobPermitsA1.show();
el.dobPermitsA2A3.show();
}
function slideTen() {
if (el.featureGroup.hasLayer(el.rheingoldJson)) {
el.featureGroup.removeLayer(el.rheingoldJson);
}
el.dobPermitsNB.hide();
el.dobPermitsA1.hide();
el.dobPermitsA2A3.hide();
el.taxLots.setSQL(sql.all);
el.taxLots.setCartoCSS(mapStyles.regular);
el.taxLots.show();
}
function initOdyssey(O) {
var map = el.map;
// O is for Odyssey
// var O = O;
var seq = O.Triggers.Sequential();
// enable keys to move slides
O.Triggers.Keys().left().then(seq.prev, seq)
O.Triggers.Keys().right().then(seq.next, seq)
// set up triggers for slide arrows
click(document.querySelectorAll('.next')).then(seq.next, seq)
click(document.querySelectorAll('.prev')).then(seq.prev, seq)
var slides = O.Actions.Slides('slides');
el.story = O.Story()
.addState(
seq.step(0),
O.Parallel(
slides.activate(0),
emitSlideChange
)
)
.addState(
seq.step(1),
O.Parallel(
el.map.actions.setZoom(17),
slides.activate(1),
emitSlideChange
)
)
.addState(
seq.step(2),
O.Parallel(
el.map.actions.setZoom(16),
slides.activate(2),
emitSlideChange
)
)
.addState(
seq.step(3),
O.Parallel(
slides.activate(3),
emitSlideChange
)
)
.addState(
seq.step(4),
O.Parallel(
el.map.actions.panTo(el.colony),
slides.activate(4),
L.marker(el.colony).actions.addRemove(el.map),
emitSlideChange
)
)
.addState(
seq.step(5),
O.Parallel(
slides.activate(5),
L.marker(el.colony).actions.addRemove(el.map),
emitSlideChange
)
)
.addState(
seq.step(6),
O.Parallel(
el.map.actions.panTo(el.colony),
slides.activate(6),
L.marker(el.colony).actions.addRemove(el.map),
emitSlideChange
)
)
.addState(
seq.step(7),
O.Parallel(
el.map.actions.panTo(el.linden),
slides.activate(7),
L.marker(el.linden).actions.addRemove(el.map),
emitSlideChange
)
)
.addState(
seq.step(8),
O.Parallel(
el.map.actions.panTo(el.linden),
slides.activate(8),
L.marker(el.linden).actions.addRemove(el.map),
emitSlideChange
)
)
.addState(
seq.step(9),
O.Parallel(
el.map.actions.panTo(el.bushwick),
el.map.actions.setZoom(15),
slides.activate(9),
emitSlideChange
)
);
el.story.go(0);
}
function init() {
initMap();
loadCdbData();
initOdyssey(O);
listenSlideChange();
}
return {
init : init,
el : el
}
})(window, document, jQuery, O);
window.addEventListener('DOMContentLoaded', function(){
app.odysseyTest.init();
});
</script>
</body>
</html>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment