Public
Edited
Nov 4, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const container = html`<div style="height:600px;">`;
yield container;
const map = (container.value = new maplibregl.Map({
container,
boxZoom: true,
pitch: 0,
bearing: 0,
maplibreLogo: true,
center: [110.4342213, -7.8714344],
zoom: 10.25,
hash: true,
// style: "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json",
// check https://basemaps.cartocdn.com/basemaplist.js for more
style: {
version: 8,
sources: {
"carto-dark": {
type: "raster",
tiles: [
"https://a.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}@2x.png",
"https://b.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}@2x.png",
"https://c.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}@2x.png",
"https://d.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}@2x.png"
]
}
},
layers: [
{
id: "carto-dark-layer",
source: "carto-dark",
type: "raster",
minzoom: 0,
maxzoom: 22
}
]
},
scrollZoom: true
}));

map.addControl(new maplibregl.NavigationControl());

map.on("load", () => {
map.addSource("jogja-admin", {
type: "geojson",
data: admin
});
map.addSource("jogja-districts", {
type: "geojson",
data: districts
});
map.addSource("jogja-villages", {
type: "geojson",
data: selectedData
});
map.addSource("dot", {
type: "geojson",
data: point_dist(selectedData, "R501A1", 0.01)
});

map.addLayer({
id: "admin",
type: "line",
source: "jogja-admin",
paint: {
"line-color": "white",
"line-width": 1.8,
"line-opacity": 1
}
});
map.addLayer({
id: "districts",
type: "line",
source: "jogja-districts",
paint: {
"line-color": "white",
"line-width": 1,
"line-opacity": 0.8
}
});
map.addLayer({
id: "villages",
type: "line",
source: "jogja-villages",
paint: {
"line-color": "#877b59",
"line-width": 0.8,
"line-opacity": 0.6
}
});

// where the magic happens
// Step 1: inner glow layer
map.addLayer({
id: "glowy-things-1",
type: "circle",
source: "dot",
paint: {
// "circle-radius": 10,
"circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 10, 22, 80],
// "circle-color": "#39FF14", // greenish
"circle-color": "#FFD700", // gold
"circle-blur": 3,
"circle-opacity": 0.4
}
});

// Step 2: brighter neon green color, blur of 3, and opacity of 0.4
map.addLayer({
id: "glowy-things-2",
type: "circle",
source: "dot",
paint: {
// "circle-radius": 5,
"circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 5, 22, 45],
// "circle-color": "#7FFF00", // neon green
"circle-color": "#FFFF00", // yellow
"circle-blur": 3,
"circle-opacity": 0.4
}
});

// Step 3: Duplicate layer with radius of 1, white color, zero blur, and opacity of 1
map.addLayer({
id: "glowy-things-3",
type: "circle",
source: "dot",
paint: {
// "circle-radius": 1,
"circle-radius": ["interpolate", ["linear"], ["zoom"], 10, 1, 22, 10],
"circle-color": "#FFFFFF",
"circle-blur": 0,
"circle-opacity": 0.9
}
});
});

invalidation.then(() => map.remove());
}
Insert cell
Insert cell
Insert cell
Plot.plot({
projection: {
type: "reflect-y",
domain: selectedData
},
marks: [
Plot.frame({ fill: "black" }), // going dark mode
Plot.geo(admin, { stroke: "white" }),
Plot.geo(districts, { stroke: "white", strokeOpacity: 0.6 }),
Plot.geo(selectedData, { stroke: "white", strokeOpacity: 0.3 }),
firefly(point_dist(selectedData, "R501A1", 0.01))
],
margin: 20,
width: 975,
height: 610
})
Insert cell
function firefly(data, attribute) {
const divisionFactor = 4;
function createPlot(radius, fill, fillOpacity, blur) {
return Plot.geo(data, {
r: radius,
fill: fill,
fillOpacity: fillOpacity,
blur: blur
});
}

let glowyThings1 = createPlot(10 / divisionFactor, "#FFD700", 0.4, 3);
let glowyThings2 = createPlot(5 / divisionFactor, "#FFFF00", 0.4, 3);
let glowyThings3 = createPlot(1 / divisionFactor, "#FFFFFF", 1, 0);

return [glowyThings1, glowyThings2, glowyThings3];
}
Insert cell
Insert cell
point_dist = (data, attribute, factor = 1) => {
// Initialize an empty FeatureCollection for the points
let points = turf.featureCollection([]);

// For each polygon, generate a number of random points proportional to its attribute
for (let polygon of data.features) {
let value = polygon.properties[attribute];

// Define the number of points for this polygon
// let numberOfPoints = Math.round(value / 1000);
let numberOfPoints = Math.round(value * factor);

// Generate a set of random points within this polygon
// use greedy algorithm to ensure the number of points is accuarate
for (let i = 0; i < numberOfPoints; i++) {
let point;
do {
point = turf.randomPoint(1, { bbox: turf.bbox(polygon) }).features[0];
} while (!turf.booleanPointInPolygon(point, polygon));

point.properties.data = numberOfPoints;

// Add this point to the collection
points.features.push(point);
}
}

return points;
}
Insert cell
Insert cell
Insert cell
selectedData = filteredAttributes(["R501A1"])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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