Published
Edited
Nov 21, 2019
24 stars
Insert cell
Insert cell
Insert cell
L = require('leaflet@1.2.0')
Insert cell
html`<link href='${resolve('leaflet@1.2.0/dist/leaflet.css')}' rel='stylesheet' />`
Insert cell
Insert cell
ElectoralDistricts = FileAttachment("feds.json").json()
Insert cell
Insert cell
Insert cell
map = {
// You'll often see Leaflet examples initializing a map like L.map('map'),
// which tells the library to look for a div with the id 'map' on the page.
// In Observable, we instead create a div from scratch in this cell (called "map")
let container = DOM.element('div', { style: `width:${width}px;height:${width/1.6}px` });
// This component utilizes "yield" which pauses the execution of this code block
// returns the value of container back to the notebook which allows the
// div to be placed on the page. This is important, because Leaflet uses
// the div's .offsetWidth and .offsetHeight (used to get current size of the div)
// to size the map. If I were to only return the container at the end of this method,
// Leaflet might get the wrong idea about the map's size.
yield container;
// Now we create a map object and add a layer to it.
let map = L.map(container).setView([49.2527, -123.1207], 11.5); // initializes the map, sets zoom & coordinates
let osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map); //This adds the tile layer to the map. The map dynamically loads tiles based on current view the experience smooth and seamless. Make sure to add attribution to your map.
}
Insert cell
Insert cell
Insert cell
VanAreas = FileAttachment("VancouverAreaSize.json").json()
Insert cell
VanAreasMap = {

let container = DOM.element('div', { style: `width:${width}px;height:${width/1.6}px` });
yield container;

let map = L.map(container);// Did not set view because we are using "fit bounds" to get the polygons to determine this
let osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
let VanAreasLayer = L.geoJson(VanAreas, { //instantiates a new geoJson layer using built in geoJson handling
weight: 2, //Attributes of polygons including the weight of boundaries and colors of map.
color: "#432",
}).bindPopup(function (Layer) { //binds a popup when clicking on each polygon to access underlying data
return Layer.feature.properties.NAME;
}).addTo(map); //Adds the layer to the map.
map.fitBounds(VanAreasLayer.getBounds()); //finds bounds of polygon and automatically gets map view to fit (useful for interaction and not having to 'cook' the map zoom and coordinates as in map instantiation
}
Insert cell
Insert cell
Insert cell
Insert cell
Dots = {
//define our map size
const width = 600
const height = 400
// This is a Observables specific convenience method which creates an SVG in the DOM.
const svg = d3.select(DOM.svg(width, height))
//projection this projects out points into space and fits the viewport based on
const projection = d3.geoMercator().fitExtent([[50,50], [width-50, height-50]], Wcities) ;
const points = svg.selectAll('circle')//Selects all circles (haven't been created yet, I know its confusing)
.data(Wcities.features) //loads the data we need - In this case our flat array of objects of features
.join('circle') //creates a circle for each element in the array
.attr("fill", "steelblue") // colors it blue
.attr("cx", d => projection(d.geometry.coordinates)[0]) // forEach element takes the coordinates and *projects*
.attr("cy", d => projection(d.geometry.coordinates)[1]) // then we index this array for the long(x) and lat(y)
.attr("r", 5) //radius of the circle
.on('mouseover', function() { //function to add mouseover event
d3.select(this).transition() //D3 selects the object we have moused over in order to perform operations on it
.duration('150') //how long we are transitioning between the two states (works like keyframes)
.attr("fill", "red") //change the fill
.attr('r', 10) //change radius
})
.on('mouseout', function() { //reverse the action based on when we mouse off the the circle
d3.select(this).transition()
.duration('150')
.attr("fill", "steelblue")
.attr('r', 5)
});
//Add text description for each city
const text = svg.selectAll('text')
.data(Wcities.features)
.join('text')
.attr("font-size", '9')
.attr("dy", 3) //shift on y-axis to align
.attr("x", d => projection(d.geometry.coordinates)[0]+5) //Offset to make it visible
.attr("y", d => projection(d.geometry.coordinates)[1])
.text(d => d.properties.Name) //Set teh text according to data
return svg.node();
}
Insert cell
Insert cell
WesternCanadianCities = {
let container = DOM.element('div', { style: `width:${width}px;height:${width/1.6}px` });
yield container;

let map = L.map(container).setView([53.2527, -123.1207], 5).fitBounds(L.geoJson(Wcities).getBounds());
let osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

//initialize svg to add to map
L.svg({clickable:true}).addTo(map)// we have to make the svg layer clickable
const overlay = d3.select(map.getPanes().overlayPane)
const svg = overlay.select('svg').attr("pointer-events", "auto")
const Dots = svg.selectAll('circle')
.attr("class", "Dots")
.data(Wcities.features)
.join('circle')
.attr("id", "dotties")
.attr("fill", "steelblue")
.attr("stroke", "black")
//Leaflet has to take control of projecting points. Here we are feeding the latitude and longitude coordinates to
//leaflet so that it can project them on the coordinates of the view. Notice, we have to reverse lat and lon.
//Finally, the returned conversion produces an x and y point. We have to select the the desired one using .x or .y
.attr("cx", d => map.latLngToLayerPoint([d.geometry.coordinates[1],d.geometry.coordinates[0]]).x)
.attr("cy", d => map.latLngToLayerPoint([d.geometry.coordinates[1],d.geometry.coordinates[0]]).y)
.attr("r", 5)
.on('mouseover', function() { //function to add mouseover event
d3.select(this).transition() //D3 selects the object we have moused over in order to perform operations on it
.duration('150') //how long we are transitioning between the two states (works like keyframes)
.attr("fill", "red") //change the fill
.attr('r', 10) //change radius
})
.on('mouseout', function() { //reverse the action based on when we mouse off the the circle
d3.select(this).transition()
.duration('150')
.attr("fill", "steelblue")
.attr('r', 5)
});
const update = () => Dots
.attr("cx", d => map.latLngToLayerPoint([d.geometry.coordinates[1],d.geometry.coordinates[0]]).x)
.attr("cy", d => map.latLngToLayerPoint([d.geometry.coordinates[1],d.geometry.coordinates[0]]).y)

map.on("zoomend", update)

}
Insert cell
Insert cell
D3VanAreasMap = {

let container = DOM.element('div', { style: `width:${width}px;height:${width/1.6}px` });
yield container;

let map = L.map(container).setView([49.2527, -123.1207], 11.5);
let osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
//initialize svg to add to map
L.svg({clickable:true}).addTo(map) // we have to make the svg layer clickable
//Create selection using D3
const overlay = d3.select(map.getPanes().overlayPane)
const svg = overlay.select('svg').attr("pointer-events", "auto")
// create a group that is hidden during zooming
const g = svg.append('g').attr('class', 'leaflet-zoom-hide')
// Use Leaflets projection API for drawing svg path (creates a stream of projected points)
const projectPoint = function(x, y) {
const point = map.latLngToLayerPoint(new L.LatLng(y, x))
this.stream.point(point.x, point.y)
}
// Use d3's custom geo transform method to implement the above
const projection = d3.geoTransform({point: projectPoint})
// creates geopath from projected points (SVG)
const pathCreator = d3.geoPath().projection(projection)
const areaPaths = g.selectAll('path')
.data(VanAreas.features)
.join('path')
.attr('fill-opacity', 0.3)
.attr('stroke', 'black')
.attr("z-index", 3000)
.attr('stroke-width', 2.5)
.on("mouseover", function(d){
d3.select(this).attr("fill", "red")
})
.on("mouseout", function(d){
d3.select(this).attr("fill", "black")
})
// Function to place svg based on zoom
const onZoom = () => areaPaths.attr('d', pathCreator)
// initialize positioning
onZoom()
// reset whenever map is moved
map.on('zoomend', onZoom)
}
Insert cell
Insert cell
html`<style>
.leaflet-overlay-pane svg path{
pointer-events: auto;
}
</style?>`
Insert cell
Insert cell
Insert cell
Insert cell
VegaLite = require('vega-embed@6')
Insert cell
fedUnemployment = d3.csv("https://gist.githubusercontent.com/mbarison/8ab5b4ef36ade1c6cfd05fb2a680d8f0/raw/4a54f7934ceab051a61fda996c8d58da72693fb8/canada_fed_unemp.csv")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more