Explore depth of future innundations resuting from Sea Level Rise and likely future storm events for the San Francisco Bay. This data is based on following study:
Radke, J. D., G. S. Biging, M. Schmidt-Poolman, H. Foster, E. Roe, Y. Ju, O. Hoes, T. Beach, A. Alruheil, L. Meier, W. Hsu, R. Neuhausler, W. Fourt (University of California, Berkeley). 2016. An Assessment of the Climate Change Vulnerability of the Natural Gas Transmission Infrastructure for the San Francisco Bay Area, Sacramento—San Joaquin Delta, and Coastal California. California Energy Commission.
While the original study modelled the San Francsico Bay at resolutions that ranged between 3 square meters and 12 square meters, the data on Cal-Adapt has been resampled to a spatial resolution of 3 square meters and shows the maximum innundation depth during the 72 hour simulation period.
####Disclaimer: This data visualization is a personal exercise. Any views or ideas represented here are personal and do not represent those of people, institutions or organizations that I may be associated with.
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//d3js.org/d3-tile.v0.0.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-legend/2.9.0/d3-legend.min.js"> </script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
.counties {
fill: none;
stroke: #777;
stroke-width: 2;
}
.map {
width: 100%;
height: 100%;
background-color: #f6f6f6;
position: relative;
min-height: 600px;
}
svg {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
overflow: hidden;
}
.attribution-control {
position: absolute;
z-index: 3;
bottom: 5px;
right: 5px;
color: #fff;
font-size: 0.8em;
background: rgba(0,0,0,0.5);
padding: 0 5px;
}
.attribution-control a {
color: #fff;
}
.legend {
position: absolute;
z-index: 10;
top: 0;
left: 0;
font-family: sans-serif;
font-size: 15px;
}
.legend h3 {
margin: 0;
}
.legend select {
margin: 0;
font-family: sans-serif;
font-size: 15px;
font-style: bold;
}
.legendLinear text {
font-size: 1em;
}
</style>
</head>
<body>
<div class="map">
<div class="legend">
<h3>Depth of Innundation (in meters) for near 100-yr storm event</h3>
<select id = "opts">
<option value="0_0m">with no Sea Level Rise</option>
<option value="0_5m">with 0.5 mt of Sea Level Rise</option>
<option value="1_0m">with 1 mt of Sea Level Rise</option>
<option value="1_41m">with 1.41 mt of Sea Level Rise</option>
</select>
</div>
<div class="attribution-control">
<span>Sea Level Rise: <a href="beta.cal-adapt.org" target="_blank">Cal-Adapt</a>, Satellite: © <a href='https://www.mapbox.com/about/maps/'>Mapbox</a> © <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> © <a href="https://www.digitalglobe.com/">DigitalGlobe</a>
</div>
<svg></svg>
</div>
<script>
// Tile providers
var tileProviders = {
mapboxSatellite: tileUrl("https://{s}.tiles.mapbox.com/v4/mapbox.streets-satellite/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibXVraHR5YXIiLCJhIjoiZHdCSFRKNCJ9.-bpAlV1GXhC5qWRpI8QOVw"),
slr: tileUrl("https://api.cal-adapt.org/tiles/slr_sfbay_{slrScenario}_jdr/{z}/{x}/{y}.png?style=slr")
};
// Get width and height of map
var mapWindow = document.getElementsByClassName('map')[0];
var width = mapWindow.offsetWidth;
var height =mapWindow.offsetHeight;
// Initialize map elements
var pi = Math.PI,
tau = 2 * pi;
// Initialize the projection to fit the world in a 1×1 square centered at the origin.
var projection = d3.geoMercator()
.scale(1 / tau)
.translate([0, 0]);
var path = d3.geoPath()
.projection(projection);
var tile = d3.tile()
.size([width, height]);
var zoom = d3.zoom()
.scaleExtent([1 << 16, 1 << 24])
.on("zoom", zoomed);
var svg = d3.select("svg");
// Initialize defs element to hold filters and clip paths
var defs = svg.append("defs");
// Add basemap grayscale filter
var filterBasemap = defs.append("filter")
.attr("id", "basemapFilter")
.append("feColorMatrix");
filterBasemap
.attr("type", "saturate")
.attr("values",0);
// Add clip path
var countiesClipPath = defs.append("clipPath").attr("id", "clip").append('path');
// Add county borders
var counties = svg.append('path').attr("class", "counties");
// Initialize map tiles
var satelliteImages = tileImages(tile).url(tileProviders.mapboxSatellite);
var satelliteRaster = svg.append("g")
.classed('satellite', true)
.attr("clip-path", "url(#clip)")
.attr("filter", "url(#basemapFilter)")
.append("g")
.style('opacity', 0.5);
var slrImages = tileImages(tile).url(tileProviders.slr);
var slrRaster = svg.append("g")
.classed('slr', true)
.style('opacity', 0.8)
.append("g");
// Compute the projected initial center.
var center = projection([-122.244, 37.843]);
//countiesClipPath url
var url='https://api.cal-adapt.org/api/counties/?intersects={%22type%22:%22Polygon%22,%22coordinates%22:[[[-122.776880212876961,38.340314875516867],[-121.69175774422628,38.32283628487383],[-121.718794306364515,37.338284374515446],[-122.796071544122455,37.36843682447212],[-122.796071544122455,37.36843682447212],[-122.776880212876961,38.340314875516867]]]}&srs=4326';
d3.json(url, function(error, features) {
if (error) throw error;
// Apply a zoom transform equivalent to projection.{scale,translate,center}.
svg
.call(zoom)
.call(zoom.transform, d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(1 << 17)
.translate(-center[0], -center[1]));
countiesClipPath
.attr("d", path(features));
counties
.attr("d", path(features));
});
// Helper Functions
function zoomed() {
var transform = d3.event.transform;
var tiles;
if (transform) {
tiles = tile
.scale(transform.k)
.translate([transform.x, transform.y])();
}
countiesClipPath
.attr("transform", transform)
.style("stroke-width", 1 / transform.k);
counties
.attr("transform", transform)
.style("stroke-width", 1 / transform.k);
satelliteRaster.call(satelliteImages);
slrRaster.call(slrImages);
}
function stringify(scale, translate) {
var k = scale / 256, r = scale % 1 ? Number : Math.round;
return "translate(" + r(translate[0] * scale) + "," + r(translate[1] * scale) + ") scale(" + k + ")";
}
var slrScenario = '0_0m'; //default value
function tileUrl(pattern){
return function (d){
return pattern
.replace("{x}", d[0])
.replace("{y}", d[1])
.replace("{z}", d[2])
.replace("{s}", ["a", "b", "c"][Math.random() * 3 | 0])
.replace("{slrScenario}", slrScenario);
};
}
function getImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(url);
img.onerror = () => reject(url);
img.src = url;
});
}
function tileImages(tile){
var url = tileProviders.mapboxSatellite;
function urlExists(d) {
var el = this;
var src = url(d);
getImage(src)
.then((data) => {
d3.select(el).attr('xlink:href', src);
})
.catch((error) => {
// do nothing
});
}
var images = function (raster){
var tiles = tile();
var image = raster
.attr("transform", "scale(" + tiles.scale + ")translate(" + tiles.translate + ")")
.selectAll("image")
.data(tiles, function(d) { return d; });
image.exit()
.remove();
image.enter().append("image")
.attr("width", 1)
.attr("height", 1)
.attr("opacity", 1)
.attr("x", function(d) { return d[0]; })
.attr("y", function(d) { return d[1]; })
.each(urlExists);
};
images.url = function (_){
return arguments.length ? (url = _, images) : url;
}
return images;
}
// SLR scenario picker
d3.select('#opts')
.on('change', function() {
slrScenario = d3.select(this).property('value');
slrRaster.selectAll('image').remove();
slrRaster.call(slrImages);
});
// Legend
var linear = d3.scaleOrdinal()
.domain(['0.5', '1', '1.5', '2', '2.5', '3', '3.5', '4', '>4' ])
.range([ '#ffffd9','#edf8b1','#c7e9b4','#7fcdbb','#41b6c4','#1d91c0','#225ea8','#253494','#081d58']);
svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(20,50)");
var legendLinear = d3.legendColor()
.shapeWidth(20)
.scale(linear);
svg.select(".legendLinear")
.call(legendLinear);
d3.selectAll('.cell rect')
.style('opacity', 0.8);
// Start moving West after 8 seconds.
// setTimeout(function (){
// // Assume 30 FPS.
// var frameTime = 1000 / 30;
// svg.transition().duration(1000).call(zoom.translateBy, -122.378998, 37.1);
// svg.transition().duration(1000).call(zoom.scaleBy, 13);
// var slrOpts = document.getElementById("opts").options;
// var select = document.getElementById("opts");
// var i = 0;
// var animationTimer = setInterval(function(){
// if (i >= slrOpts.length) {
// clearInterval(animationTimer);
// }
// slrScenario = slrOpts[i].value;
// i = i + 1;
// slrRaster.selectAll('image').remove();
// slrRaster.call(slrImages);
// }, 3000);
// }, 2000);
</script>
</body>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-tile.v0.0.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.9.0/d3-legend.min.js