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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more