Public
Edited
Feb 12, 2023
Insert cell
Insert cell
df_init = FileAttachment("asst3_google.csv").csv()
Insert cell
df = df_init.map(
function(d) {
d.geometry = JSON.parse(d.geometry.replace(/'/g, '"'));
return d
}
)
Insert cell
<div id='map' style='width: 100%; height: 30rem; z-index: 0;'></div>
<input type="range" id="radiusSlider" min="0" max="20" value="1">
Insert cell
maplibregl = {
const gl = await require("maplibre-gl@2.4.0");
const href = await require.resolve("maplibre-gl@2.4.0/dist/maplibre-gl.css");
document.head.appendChild(html`<link href=${href} rel=stylesheet>`);
return gl;
}
Insert cell
turf = {
const tf = await require("@turf/turf@5");
return tf;
}
Insert cell
map = {
var map = new maplibregl.Map({
container: 'map',
style: 'https://api.maptiler.com/maps/streets/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL',
center: [-122.169961, 37.429491],
zoom: 1,
pitch: 0,
maxBounds: [
[-122.8, 36.8],
[-121.8, 38.3]
] // restrict the view to a certain area (southwest and northeast coords)
});
map.addControl(new maplibregl.NavigationControl(), 'bottom-left'); // adds the controls
return map
}
Insert cell
svg = {
var container = map.getCanvasContainer();

var svg = d3
.select(container)
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.style("position", "absolute")
.style("z-index", 2); // make this appear above the mapbox backing layer
return svg
}
Insert cell
<style>
/* You can also set this in the initial declaration, just here for sake of explanation */
#map {
position: relative;
z-index: 0;
}
</style>
Insert cell
circle1 = {
var circle_coords = [map.getCenter().lng, map.getCenter().lat];
var drag = d3.drag()
.on('drag', function(event, d) {
var new_coords = unproject(event.x, event.y);
d3.select(this)
.attr('cx', d.x = event.x)
.attr('cy', d.y = event.y)
.datum(d=>({lat: new_coords.lat, lng: new_coords.lng, size:d.size}));
})
.on('start', function() {
d3.select(this).attr("stroke", "black");
d3.event.sourceEvent.stopPropagation();
})
.on('end',function() {
d3.select(this).attr("stroke", "#6897bb");
//d3.event.sourceEvent.stopPropagation();
});
var circle = svg.append('circle')
.attr("class", "inter-circle")
.style('z-index', 100)
.datum({lat:map.getCenter().lat, lng: map.getCenter().lng, size:1*1609.344})
.attr("cx", d => project(d.lng, d.lat).x)
.attr("cy", d => project(d.lng, d.lat).y)
.attr('r', d=> pixelValue(d.lat, d.size*1609.344, map.getZoom()))
.attr('fill', '#6897bb')
.attr('fill-opacity', 0.3)
.attr('stroke', '#6897bb')
.attr('stroke-opacity', 0.8)
.attr('stroke-width', 2)
.call(drag);
var slider = d3.select("#radiusSlider");
slider.on("input", function() {
// get the value of the slider
var value = this.value;
circle.datum().size = value*1609.344;
// update the radius of the circle
circle.attr('r', d=> pixelValue(d.lat, d.size, map.getZoom()));
});
return circle
}
Insert cell
circle1.data()
Insert cell
dots = {
let dots = svg
.selectAll("#cafe-circle")
.data({dt: df, circles: circle1.datum()})
.enter()
.append("circle")
.attr("class", "cafe-circle")
.attr("r", Math.max(map.getZoom()**2/25, 3))
.attr("opacity", d=>(inCircle(d.dt, d.circles))? 0.8 : 0.65)
.attr("fill", d=>(inCircle(d.dt, d.circles))? "#cc0000" : "#c0c0c0");
return dots
}
Insert cell
circle1
Insert cell
function project(lng,lat) {
return map.project(new maplibregl.LngLat(lng, lat));
}

Insert cell
function unproject(x,y) {
return map.unproject([x,y]);
}
Insert cell

function metersPerPixel(latitude, zoomLevel) {
var earthCircumference = 40075017;
var latitudeRadians = latitude * (Math.PI/180);
return earthCircumference * Math.cos(latitudeRadians) / Math.pow(2, zoomLevel+ 9);
};

Insert cell
function pixelValue(latitude, meters, zoomLevel) {
return meters / metersPerPixel(latitude, zoomLevel);
};
Insert cell
function inCircle(cafe, circle){
var dist = turf.distance(
turf.point(
[cafe.geometry.location.lng, cafe.geometry.location.lat]
),
turf.point(
[circle.lng, circle.lat]
),
{units: 'meters'}
)
return dist <= circle.size
}
Insert cell
function render() {
dots
.attr("cx", function (d) {
return project(d.dt.geometry.location.lng, d.dt.geometry.location.lat).x;
})
.attr("cy", function (d) {
return project(d.dt.geometry.location.lng, d.dt.geometry.location.lat).y;
})
.attr("r", Math.max(map.getZoom()**2/25, 3))
circle1
.attr("cx", d => project(d.lng, d.lat).x)
.attr("cy", d => project(d.lng, d.lat).y)
.attr("r", d=> pixelValue(d.lat, d.size, map.getZoom()))

}
Insert cell
map.getCenter()
Insert cell
{
map.on("viewreset", render);
map.on("move", render);
map.on("moveend", render);
render();
}
Insert cell
map.getBounds()
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