Published unlisted
Edited
Mar 9, 2021
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Select(allSites, {
value: allSites[0],
label: "Species"
})
Insert cell
allSites = bcSiteLocs.map(d => d.site)
Insert cell
// style = html`
// <style>
// select {
// padding: 2px 5px;
// font-size: 16px;
// max-height: 100px;
// min-width: 300px;
// }
// label {
// display: inline-block;
// min-width: 300px;
// }
// .area {
// width: ${width}px;
// height: ${img_height}px;
// }
// .area img {
// position : absolute;
// object-fit: cover;
// }
// .hovered { background-color: #e6e6e6; filter: brightness(100%) saturate(100%); border: 2px solid red;}
// .fade {filter: saturate(55%) brightness(60%) opacity(70%)}
// .container:hover .image {
// opacity: 0.4;
// }
// .im {
// opacity: 1;
// }
// .middle {
// transition: .5s ease;
// opacity: .9;
// position: absolute;
// pointer-events: none;
// text-align: center;
// z-index: 10;
// }
// .text {
// font-size: 16px;
// padding: 16px 32px;
// position: relative;
// display:block;
// font-weight:600;
// text-align: center;
// color: black;
// }
// </style>
// `
Insert cell
Insert cell
Insert cell
Insert cell
viewof sites = {
const width = 500;
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);
// .call(zoom);

const g = svg.append("g").attr("id", "map-layers");

// BC Alberts projection
// const projection = d3.geoAlbers().rotat e([126, 0]);
// projection.fitSize(
// [width, height],
// topojson.feature(BC_Midres, BC_Midres.objects.BC_Midres_latlng)
// );

// svg.call(tip);

// we want a custom projection to zoom in a bit
// const projection = d3
// .geoAlbers()
// .rotate([126, 0])
// .center([0, 50.5])
// .parallels([50, 58.5])
// .scale(5500)
// .translate([width / 3, height / 2]);
const projection = d3
.geoAlbers()
.rotate([126, 0])
.center([0, 49.8])
// .parallels([50, 60.5])
.scale(7500)
.translate([960 / 4, 600 / 2]);
let current_circle = undefined;

// generate the shapes we want - this case bc
const path = d3.geoPath().projection(projection);

// create the svg of our features
g.append("svg")
.selectAll("path")
.data(
topojson.feature(BC_Midres, BC_Midres.objects.BC_Midres_latlng).features
)
.join("path")
.attr("d", path)
.attr("fill", "#ddd")
.attr("id", "map-layers");

g.append("g")
.selectAll("path")
.data(states.features)
.join("path")
.attr("fill", d => {
return "#ddd";
})
.attr("opacity", 0.4)
.attr("d", path);

// Add stations!
g.insert("g")
.selectAll("circle")
.data(mapLocs)
.join("circle")
.attr("r", function(d) {
return d.present === "yes" ? 8 : d.present === "past" ? 8 : 6;
})
.attr("transform", function(d) {
// Project our stations to the same projeciton as our map!
return `translate(${projection([d.long, d.lat])})`;
})
// .attr("opacity", 0.6)
.attr("fill", function(d) {
return d.present === "yes"
? present_colour
: d.present === "past"
? past_colour
: none_colour;
})
.attr("fill-opacity", function(d) {
return d.present === "yes" ? 0.6 : d.present === "past" ? 0.1 : 0.6;
})
.style("stroke-opacity", function(d) {
return d.present === "yes" ? 0.8 : d.present === "past" ? 0.8 : 0;
})
.attr("stroke", function(d) {
return d.present === "yes"
? present_colour
: d.present === "past"
? past_colour
: none_colour;
})
.on("mouseover", function(event, d) {
d3.select(this).attr("stroke", "#000");
tooltip
.html(`<div>${d.site}</div><div>${d.Location}</div>`)
.style('visibility', 'visible');
})
.on('mousemove', function(event) {
tooltip
.style('top', event.pageY - 10 + 'px')
.style('left', event.pageX + 10 + 'px');
})
.on("mouseout", function(event) {
d3.select(this).attr("stroke", function(d) {
return d.present === "yes"
? present_colour
: d.present === "past"
? past_colour
: none_colour;
});
tooltip.html(``).style('visibility', 'hidden');
});
// .on("click", function(event, d) {
// if (current_circle !== undefined) {
// current_circle.attr("fill", d => "steelblue");
// }
// current_circle = d3.select(this);
// d3.select(this).attr("fill", "orange");
// const node = svg.node();
// // node.value = value = value === d.ARMS_number ? null : d.ARMS_number;
// node.value = d.site;
// // console.log(node);
// node.dispatchEvent(new CustomEvent("input"));
// // outline.attr("d", value ? path(d) : null);
// });
return Object.assign(svg.node(), { value: null });
}
Insert cell
share = chart2([speciesList])
Insert cell
speciesList
Insert cell
heatData = dataRolled.filter(d => {
return (
d.date >= d3.timeMonth(cc.range[0]) &&
d.date <= d3.timeMonth(cc.range[1]) &&
d.species === speciesList
);
})
Insert cell
chart2 = function(types) {
// console.clear();
const n = types.length;
const width = 960;
// const height = 500;
// types = ["other"];
// All speceis in types ploted
// console.log(types);
const Allsites = [
...new Set(heatData.filter(d => types.includes(d.species)).map(d => d.site))
];

console.log('species', Allsites);
// const height = Allsites.length * 20;
const height = Allsites.length * 20 < 100 ? 100 : Allsites.length * 20;
// console.log("height", height);

//Calculate margins to use

const to_chart_sites = [
...new Set(heatData.filter(d => d.species === types[0]).map(d => d.site))
];

// this will be the bottom of the top chart (based on proportion of species)
// const bh_top_chart = (height * to_chart_species.length) / Allsites.length;
// console.log("bh_top_chart", bh_top_chart);

const margin1 = { top: 20, right: 40, bottom: 40, left: 140 }; //adjust bottom
const chart_width = width - margin1.left - margin1.right;
// const chart_height = height - margin1.top - margin1.bottom;

let margin_others;

// const zoom = d3
// .zoom()
// .scaleExtent([1, Infinity])
// .translateExtent([[0, 0], [chart_width, chart_bounds]])
// .extent([[0, 0], [chart_width, chart_bounds]]);

const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

const today = new Date();
const startExt = new Date();
const endExt = new Date();

startExt.setDate(today.getDate() - 1);
endExt.setDate(today.getDate() + 1);

// HERE I CREATE A CHART (FOCUS) FOR EACH VARIABLE.
let myVariables = {};
let myLines = {};
let myYs = {};
let myXs = {};
let y;

// let focus0;
const x = d3
.scaleTime()
.range([0, chart_width])
.domain([
d3.min(heatData, d => d.date),
d3.timeMonth.ceil(+d3.max(heatData, d => d.date) + 1)
]);

const xAxis = d3.axisBottom(x);
const top_margins = [];

types.forEach((vari, i) => {
// console.log('here', i);
let yside = "y" + i;

const sites = [
...new Set(heatData.filter(d => d.species === vari).map(d => d.site))
];
// console.log(species, Allspecies);

// mutable debug = species;
let h_each_plot = (height * sites.length) / Allsites.length;
top_margins.push(h_each_plot);

let chart_bounds =
(height * sites.length) / Allsites.length - margin1.top - margin1.bottom;

y = d3
.scaleBand()
.domain(sites)
.range([chart_bounds, 0])
.padding(.2);

const yAxis = g =>
g.call(d3.axisLeft(y)).call(g => g.select(".domain").remove());

const mar_top =
i === 0
? 0
: i === 1
? top_margins[0]
: d3.sum(top_margins.slice(0, [i]));

margin_others = {
top: mar_top,
right: 20,
left: 140
}; //adjust top

let variableName = "focus" + i;

myVariables[variableName] = svg
.append("g")
.attr("class", "focus" + i)
.attr(
"transform",
`translate(${margin_others.left},${margin_others.top})`
);
// console.log(vari, variableName);
console.log('blac', heatData.filter(d => d.species === vari));
myVariables[variableName]
.selectAll('rect')
.data(heatData.filter(d => d.species === vari))
.enter()
.append('rect')
.attr("class", "line" + i)
.attr('x', d => x(d.date))
.attr('y', d => {
// console.log('i', d);
return y(d.site);
})
.attr('width', d => x(d3.timeMonth.ceil(+d.date + 1)) - x(d.date) - 4)
.attr('height', y.bandwidth())
.attr('fill', d => {
return d.count > 0 ? "#ec4977" : "#ddd";
})
// .attr("fill", "none")
.attr("clip-path", "url(#clip)");
// .style("stroke", "steelblue")
// .style("stroke-width", "1.5px");

myVariables[variableName]
.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + chart_bounds + ")")
.call(xAxis);

myVariables[variableName]
.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
});

return svg.node();
}
Insert cell
chartData
Insert cell
Insert cell
monthlySiteCount[monthlySiteCount.length - 1].date
Insert cell
monthlySiteCount
Insert cell
import { Select } from "@observablehq/inputs"
Insert cell
import { preview, previews, fishWithPics, mutable type } from "73c68590a6b8ba4c"
Insert cell
present_colour = "#0cb4f5"
Insert cell
past_colour = "#0cb4f5"
Insert cell
none_colour = "black"
Insert cell
uniqSpecies = [
...new Set(dataRolled.filter(d => d.group === type).map(d => d.species))
]
Insert cell
mapLocs = bcSiteLocs.map(e => {
e.present = "no";

if (
dataRolled.filter(
d =>
d.total > 0 &&
d.site === e.site &&
(+d.date === +d3.timeMonth(date) && d.species === speciesList)
).length > 0
) {
e.present = "yes";
} else if (
dataRolled.filter(
d =>
d.total > 0 &&
d.site === e.site &&
+d.date < +d3.timeMonth(date) &&
d.species === speciesList
).length > 0
) {
e.present = "past";
} else {
e.present = "no";
}
return e;
})
Insert cell
bcSiteLocs
Insert cell
speciesList
Insert cell
date
Insert cell
dataRolled[0]
Insert cell
dataRolled.filter(d => {
// console.log(+d.date < +d3.timeMonth(date));
return (
d.total > 0 &&
d.site === bcSiteLocs[0].site &&
+d.date < +d3.timeMonth(date) &&
d.species === speciesList
);
})
Insert cell
Insert cell
function join(lookupTable, mainTable, lookupKey, mainKey, select) {
const l = lookupTable.length,
m = mainTable.length,
output = [],
// create an index for lookup table
lookupIndex = new Map(lookupTable.map(d => [d[lookupKey], d]));

return mainTable.map(d => select(d, lookupIndex.get(d[mainKey])));
return output;
}
Insert cell
// d3.timeMonth(joinedData[0].monthly)
Insert cell
// +joinedData[0].monthly < +d3.timeMonth(date)
Insert cell
// joinedData[0].monthly
Insert cell
d3.timeMonth(date)
Insert cell
// mapData = joinedData.map(d => {
// d.presence = "no";
// if (+d.monthly === +d3.timeMonth(date) && d.class === type) {
// d.presence = "yes";
// } else if (+d.monthly < +d3.timeMonth(date) && d.class === type) {
// d.presence = "past";
// } else {
// d.presence = "no";
// }
// return d;
// })
Insert cell
speciesList
Insert cell
bcSiteLocs
Insert cell
d3.timeMonth(date)
Insert cell
dataRolled.filter(
d =>
d.site === bcSiteLocs[0].site &&
(+d.date === +d3.timeMonth(date) && d.species === speciesList)
)
Insert cell
(2291 + 76385) * 0.04 + (2291 + 76385)
Insert cell
type
Insert cell
dataRolled[0]
Insert cell
type
Insert cell
// joinedData.filter(
// d =>
// d.site === bcSiteLocs[0].site &&
// +d.monthly < +d3.timeMonth(date) &&
// d.class === type.length > 0
// )
Insert cell
// joinedData = join(bcSiteLocs, fakeData, "site", "site", (data, locs) => ({
// site: data.site,
// monthly: data.monthly,
// species: data.species,
// class: data.class,
// lat: locs.lat,
// long: locs.long
// // : locs !== undefined ? locs.name : null
// }))
Insert cell
Insert cell
interval = d3.timeMonth.every(1)
Insert cell
monthlySiteCount[monthlySiteCount.length - 1]
Insert cell
maxSitesPerMonth = d3.max(
Array.from(
d3.rollup(chartData, v => v.length, d => d.date),
([key, values]) => {
return values;
}
)
)
Insert cell
monthlySiteCount = Array.from(
d3.rollup(
chartData,
v =>
d3.sum(v, d => {
d.count;
d.total > 0 ? (d.count = 1) : (d.count = 0);
return d.count;
}),
d => d.date
),
([key, values]) => {
return {
date: new Date(key),
// datestr: key,
value: values
};
}
).sort((a, b) => a.date - b.date)
Insert cell
d3.rollup(
chartData,
v =>
d3.sum(v, d => {
d.count;
d.total > 0 ? (d.count = 1) : (d.count = 0);
return d.count;
}),
d => d.date
)
Insert cell
chartData
Insert cell
// monthlySiteCount = Array.from(sitesCountbyMonth, ([key, values]) => {
// return Array.from(values, ([month, count]) => {
// return {
// site: key,
// monthly: month,
// // datestr: key,
// value: 1
// };
// });
// }).sort((a, b) => a.monthly - b.monthly)
Insert cell
// monthlySiteCount = d3.rollup(chartData, v => v.length, d => d.date)
Insert cell
sitesCountbyMonth = d3.rollup(
chartData,
v => v.length,
d => d.site,
d => d.date
)
Insert cell
// sitesCountbyMonth = d3.rollup(
// chartData,
// v =>
// d3.sum(v, d => {
// d.count;
// d.total > 0 ? (d.count = 1) : (d.count = 0);
// return d.count;
// }),
// d => +d.date
// )
Insert cell
chartData = dataRolled.filter(d => d.species === speciesList)
Insert cell
dateFilter = new Date(date).toLocaleString("en", {
month: "numeric",
year: 'numeric'
})
Insert cell
Insert cell
height = 500
Insert cell
uniqDates = [...new Set(dataRolled.map(d => +d.date))].sort((a, b) => a - b)
Insert cell
d3 = require("d3@6", 'd3-gridding@0.1')
Insert cell
_ = require("lodash")
Insert cell
topojson = require("topojson-client@3")
Insert cell
import { bcSiteLocs, dataRolled } from 'a918fce3c1f416e8'
Insert cell
import { states } from '73c68590a6b8ba4c'
Insert cell
import { BC_Midres } from '@mbrownshoes/how-i-start-maps-in-d3'
Insert cell
import { Scrubber } from "@mbostock/scrubber"
Insert cell
img_height = 600
Insert cell
// area = {
// const area = d3.create('div').attr('class', 'area');
// // .on("mouseover", function(event, d) {
// // event.currentTarget.firstElementChild.style.borderColor = "#e8e8e8";
// // });

// return area;
// }
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
modes = d3.gridding().modes()
Insert cell
// import { preload } from '@severo/preload-a-list-of-images'
Insert cell
// import { form } from "@mbostock/form-input"
Insert cell
// import { select } from "@jashkenas/inputs"
Insert cell
// import { viewof stuff } from '3ba42860653e101c'
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