Published
Edited
May 23, 2022
1 fork
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
mapContainer = {
const mapContainer = DOM.element("div", {
style: `width:${width}px; height:${height}px`
});
yield mapContainer;
}
Insert cell
function InitMap() {
let map = L.map(mapContainer);

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);

var geojsonMarkerOptions = {
radius: 3,
fillColor: "#ff7800",
color: "#000",
weight: 1,
opacity: 1,
fillOpacity: 0.8
};

var points = L.geoJSON(PlotPoints, {
pointToLayer: function (feature, latlng) {
debugger;
return L.circleMarker(latlng, {
color: "gray"
});
}
}).addTo(map);
map.fitBounds(points.getBounds());
PointFG.addTo(map);

return points;
}
Insert cell
mutable myMap = []
Insert cell
mutable setup = true
Insert cell
{
if (setup) {
mutable setup = false;
mutable myMap = InitMap();
}
}
Insert cell
mutable SiteHighlight = ""
Insert cell
PointUpdate = {
PointFG.clearLayers();
PointFG.addLayer(points);
}
Insert cell
points = {
SiteHighlight;
return L.geoJSON(PlotPoints, {
pointToLayer: function (feature, latlng) {
debugger;
return L.circleMarker(latlng, {
color: feature.properties.SiteID == SiteHighlight ? "red" : "blue",
opacity: feature.properties.SiteID == SiteHighlight ? 1 : 0,
fillOpacity: feature.properties.SiteID == SiteHighlight ? 1 : 0
});
}
});
}
Insert cell
PointFG = L.featureGroup()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
TriangleFillLines = {
const Lines = [];
//Triangle 1 horizontal lines
for (let i in breaks) {
Lines.push({
x0: Triangle1XY[0] + breaks[i] / 2,
y0: Triangle1XY[1] + (breaks[i] * Math.sqrt(3)) / 2,
x1: Triangle1XY[0] + 1 - breaks[i] / 2,
y1: Triangle1XY[1] + (breaks[i] * Math.sqrt(3)) / 2
});
}
//Triangle 1 left leaning grids
for (let i in breaks) {
Lines.push({
x0: Triangle1XY[0] + 1 - breaks[i],
y0: Triangle1XY[1],
x1: Triangle1XY[0] + 1 / 2 - breaks[i] / 2,
y1: Triangle1XY[1] + ((1 - breaks[i]) * Math.sqrt(3)) / 2
});
}
//Triangle 1 right leaning grids
for (let i in breaks) {
Lines.push({
x0: Triangle1XY[0] + 1 - (1 - breaks[i]),
y0: Triangle1XY[1],
x1: Triangle1XY[0] + 1 / 2 + breaks[i] / 2,
y1: Triangle1XY[1] + ((1 - breaks[i]) * Math.sqrt(3)) / 2
});
}
//Triangle 2 horizontal lines
for (let i in breaks) {
Lines.push({
x0: Triangle2XY[0] + breaks[i] / 2,
y0: Triangle2XY[1] + (breaks[i] * Math.sqrt(3)) / 2,
x1: Triangle2XY[0] + 1 - breaks[i] / 2,
y1: Triangle2XY[1] + (breaks[i] * Math.sqrt(3)) / 2
});
}
//Triangle 2 left leaning grids
for (let i in breaks) {
Lines.push({
x0: Triangle2XY[0] + 1 - breaks[i],
y0: Triangle2XY[1],
x1: Triangle2XY[0] + 1 / 2 - breaks[i] / 2,
y1: Triangle2XY[1] + ((1 - breaks[i]) * Math.sqrt(3)) / 2
});
}
//Triangle 2 right leaning grids
for (let i in breaks) {
Lines.push({
x0: Triangle2XY[0] + 1 - (1 - breaks[i]),
y0: Triangle2XY[1],
x1: Triangle2XY[0] + 1 / 2 + breaks[i] / 2,
y1: Triangle2XY[1] + ((1 - breaks[i]) * Math.sqrt(3)) / 2
});
}
//Rhombus left leaning grids
for (let i in breaks) {
Lines.push({
x0: Triangle1XY[0] + 1.1 + breaks[i] / 2,
y0: Triangle2XY[1] + Math.sqrt(3) * 0.1 + (Math.sqrt(3) / 2) * breaks[i],
x1: Triangle1XY[0] + 1.1 - 1 / 2 + breaks[i] / 2,
y1:
Triangle2XY[1] +
Math.sqrt(3) * 0.1 +
(Math.sqrt(3) / 2) * breaks[i] +
Math.sqrt(3) / 2
});
}
//Rhombus right leaning grids
for (let i in breaks) {
Lines.push({
x0: Triangle1XY[0] + 1.1 - breaks[i] / 2,
y0: Triangle2XY[1] + Math.sqrt(3) * 0.1 + (Math.sqrt(3) / 2) * breaks[i],
x1: Triangle1XY[0] + 1.1 + 1 / 2 - breaks[i] / 2,
y1:
Triangle2XY[1] +
Math.sqrt(3) * 0.1 +
(Math.sqrt(3) / 2) * breaks[i] +
Math.sqrt(3) / 2
});
}

return Lines;
}
Insert cell
Insert cell
toolTip = d3.select("body").append("div").attr("class", "toolTip")
Insert cell
function HighlightMatch(el) {
const color = ColorGroups.find((obj) => {
return obj.Group === el.Group;
}).color;
d3.selectAll("circle").each(function (d, i) {
if (d.SiteID == el.SiteID) {
d3.select(this).style("fill", color);
d3.select(this.parentNode).raise();
}
});
d3.selectAll("rect").each(function (d, i) {
if (d.SiteID == el.SiteID) {
d3.select(this).style("fill", color).raise();
d3.select(this.parentNode).raise();
}
});
d3.selectAll("path").each(function (d, i) {
if (d.SiteID == el.SiteID) d3.select(this).style("fill", color).raise();
});
}
Insert cell
function MarkerSelect(d) {
const shape = ColorGroups.find((obj) => {
return obj.Group === d.Group;
}).shape;
const color = ColorGroups.find((obj) => {
return obj.Group === d.Group;
}).color;
var elem = d3.select(this);
if (shape == "rectangle") {
//rectangle
elem
.append("rect")
.attr("width", 5)
.attr("height", 5)
.attr("x", (d) => x(d.x) - 2.5)
.attr("y", (d) => y(d.y) - 2.5)
.attr("fill", color);
}
if (shape == "triangle") {
//triangle
//svg does not have triangle shape , so we use path to draw it
elem
.append("path")
.attr("d", "M 100 100 L 300 100 L 200 300 z")
.attr(
"transform",
`translate(${x(d.x) - 0.05 * 200}, ${y(d.y) - 0.05 * 175}) scale(0.05)`
)
.style("fill", function () {
return color;
});
}
if (shape == "circle") {
//circle
elem
.append("circle")
.attr("r", 3)
.attr("cx", (d) => x(d.x))
.attr("cy", (d) => y(d.y))
.style("fill", function () {
return color;
});
}
}
Insert cell
function linspace(start, stop, num, endpoint = true) {
const div = endpoint ? num - 1 : num;
const step = (stop - start) / div;
return Array.from({ length: num }, (_, i) => start + step * i);
}
Insert cell
breaks = [0.2, 0.4, 0.6, 0.8]
Insert cell
Triangle = [
//Triangle 1
{
x0: Triangle1XY[0],
y0: Triangle1XY[1],
x1: Triangle1XY[0] + 1,
y1: Triangle1XY[1]
},
{
x0: Triangle1XY[0] + 1 / 2,
y0: Triangle1XY[1] + Math.sqrt(3) / 2,
x1: Triangle1XY[0] + 1,
y1: Triangle1XY[1]
},
{
x0: Triangle1XY[0] + 1 / 2,
y0: Triangle1XY[1] + Math.sqrt(3) / 2,
x1: Triangle1XY[0],
y1: Triangle1XY[1]
},
//Triangle 2
{
x0: Triangle2XY[0],
y0: Triangle2XY[1],
x1: Triangle2XY[0] + 1,
y1: Triangle2XY[1]
},
{
x0: Triangle2XY[0] + 1 / 2,
y0: Triangle2XY[1] + Math.sqrt(3) / 2,
x1: Triangle2XY[0] + 1,
y1: Triangle2XY[1]
},
{
x0: Triangle2XY[0] + 1 / 2,
y0: Triangle2XY[1] + Math.sqrt(3) / 2,
x1: Triangle2XY[0],
y1: Triangle2XY[1]
},
// Combined
{
x0: Triangle1XY[0] + 1.1,
y0: Triangle2XY[1] + Math.sqrt(3) * 0.1,
x1: Triangle1XY[0] + 1.1 - 1 / 2,
y1: Triangle2XY[1] + Math.sqrt(3) * 0.1 + Math.sqrt(3) / 2
},
{
x0: Triangle1XY[0] + 1.1,
y0: Triangle2XY[1] + Math.sqrt(3) * 0.1,
x1: Triangle1XY[0] + 1.1 + 1 / 2,
y1: Triangle2XY[1] + Math.sqrt(3) * 0.1 + Math.sqrt(3) / 2
},
{
x0: Triangle1XY[0] + 1.1,
y0: Triangle2XY[1] + Math.sqrt(3) * 0.1 + Math.sqrt(3),
x1: Triangle1XY[0] + 1.1 - 1 / 2,
y1: Triangle2XY[1] + Math.sqrt(3) * 0.1 + Math.sqrt(3) / 2
},
{
x0: Triangle1XY[0] + 1.1,
y0: Triangle2XY[1] + Math.sqrt(3) * 0.1 + Math.sqrt(3),
x1: Triangle1XY[0] + 1.1 + 1 / 2,
y1: Triangle2XY[1] + Math.sqrt(3) * 0.1 + Math.sqrt(3) / 2
}
]
Insert cell
data = FileAttachment("Piper@1.csv").csv({ typed: true })
Insert cell
dataEq = data.map((x) => {
x.Potassium /= 39.1;
x.Sodium /= 22.9;
x.Bicarbonate /= 61.02;
x.Carbonate /= 30;
x.Calcium /= 20.04;
x.Magnesium /= 12.15;
x.Chloride /= 35.45;
x.Sulfate /= 48.03;
return x;
})
Insert cell
Triangle1XY = [0.2, 0.2]
Insert cell
Triangle2XY = [1.4, 0.2]
Insert cell
CationWeight = dataEq.map((x) => {
const sum = x.Magnesium + x.Calcium + x.Sodium + x.Potassium;
return {
SiteID: x.SiteID,
Date: x.Date,
Group: x.Group,
a: (x.Sodium + x.Potassium) / sum,
b: x.Magnesium / sum,
c: x.Calcium / sum
};
})
Insert cell
CationXY = CationWeight.map((el) => {
return {
SiteID: el.SiteID,
Date: el.Date,
Group: el.Group,
x: Triangle1XY[0] + (1 / 2) * (2 * el.b + el.c / (el.a + el.b + el.c)),
y: Triangle1XY[1] + ((Math.sqrt(3) / 2) * el.c) / (el.a + el.b + el.c)
};
})
Insert cell
AnionWeight = dataEq.map((x) => {
const sum = x.Carbonate + x.Bicarbonate + x.Chloride + x.Sulfate;
return {
SiteID: x.SiteID,
Date: x.Date,
Group: x.Group,
a: x.Chloride / sum,
b: x.Sulfate / sum,
c: (x.Carbonate + x.Bicarbonate) / sum
};
})
Insert cell
AnionXY = AnionWeight.map((el) => {
return {
Date: el.Date,
SiteID: el.SiteID,
Group: el.Group,
x: Triangle2XY[0] + (1 / 2) * (2 * el.b + el.c / (el.a + el.b + el.c)),
y: Triangle2XY[1] + ((Math.sqrt(3) / 2) * el.c) / (el.a + el.b + el.c)
};
})
Insert cell
MixedXY = CationXY.map((el, i) => {
const x0 = el.x;
const y0 = el.y;
const x1 = AnionXY[i].x;
const y1 = AnionXY[i].y;
const x =
(y1 + Math.sqrt(3) * x1 - y0 + Math.sqrt(3) * x0) / (2 * Math.sqrt(3));
return {
Date: el.Date,
SiteID: el.SiteID,
Group: el.Group,
x: x,
y: Math.sqrt(3) * x + y0 - Math.sqrt(3) * x0
};
})
Insert cell
height = 600
Insert cell
width = 600
Insert cell
x = d3.scaleLinear().domain([0, 2.4]).range([0, width])
Insert cell
y = d3.scaleLinear().domain([0, 2.4]).range([height, 0])
Insert cell
xAxis = (g) =>
g.attr("transform", `translate(0,${height})`).call(d3.axisBottom(x))
Insert cell
yAxis = (g) => g.attr("transform", `translate(${width},0)`).call(d3.axisLeft(y))
Insert cell
margin = ({ top: 25, right: 20, bottom: 35, left: 40 })
Insert cell
Coords = data.map((e) => {
let latlng = UTMtoLatLng([e.Easting, e.Northing]);
return {
type: "Feature",
geometry: {
type: "Point",
coordinates: [latlng.lng, latlng.lat]
},
properties: { SiteID: e.SiteID }
};
})
Insert cell
PlotPoints = ({
name: "MyFeatureType",
type: "FeatureCollection",
features: Coords
})
Insert cell
function UTMtoLatLng(coords) {
var LatLongCoords = proj.forward(coords);
return new L.LatLng(LatLongCoords[1], LatLongCoords[0]);
}
Insert cell
projTo = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"
Insert cell
projFrom = "+proj=utm +zone=13 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs "
Insert cell
proj = proj4(projFrom, projTo)
Insert cell
proj4 = require("proj4")
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