Public
Edited
Dec 12, 2023
1 fork
1 star
Insert cell
Insert cell
hotel_100_2022.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
import {Plot} from "@observablehq/plot";

Insert cell
import {vl} from "@vega/vega-lite-api-v5"
Insert cell
import {input} from '@observablehq/inputs';

Insert cell
import {select} from '@observablehq/inputs';

Insert cell
data = FileAttachment("hotel_100_2022.csv").csv()

Insert cell
// ORIGINAL
name2 =
FileAttachment("hotel_100_2022.csv").csv().then(hotels_data => {

const groupedData = Array.from(d3.rollup(hotels_data, v => v.length, d => d.Country))
.map(([Country, count]) => ({Country, count}))
.sort((a, b) => b.count - a.count);
//const tooltip = (d) => `Hotel: ${d.Hotel}\nRank: ${d.Rank}`;
const tooltip = (d) => `Hotel: ${d.Hotel}\nRank: ${d.Rank}\nCountry: ${d.Country}`;

const sortedCountries = groupedData.slice().sort((a, b) => b.count - a.count).map(d => d.Country);

const regions = ["Africa", "Caribbean", "Europe", "Asia", "Latin America", "Middle East", "North America", "Oceania", "Southeast Asia"]

const hotelCountByCountry = d3.rollups(
hotels_data,
group => group.length,
d => d.Country
);

const highestRankedHotels = d3.rollup(hotels_data,
(v) => v.reduce((a, b) => a.Rank < b.Rank ? a : b),
d => d.Country
);

const title = "Number of Top 100 Hotels In Each Country";

// Create the plot
return Plot.plot({
title: title,
y: {
label: "→ Country",
axis: "left",
domain: sortedCountries,
tickRotate: 45,
},
x: {
label: "→ Hotel Count",
axis: "bottom"
},
width: 800,
height: 900,
marginBottom: 125,
marginLeft: 145,
marks: [
Plot.barX(
thebesthotel,
Plot.groupY({ x: "count" }, { y: "Country", fill: "Region", tip: true })
),
Plot.ruleX([0])
],
color: { legend: true},
});
});

// origina;
Insert cell
Insert cell
options = ["Option 1", "Option 2", "Option 3", "Option 4"];

Insert cell
mutable selectedHotelName = null;

Insert cell
html`
<label>
<input type="radio" name="xAxis" value="Rank" checked> Hotel Rank
</label>
<label>
<input type="radio" name="xAxis" value="past_rank"> Past Hotel Rank
</label>
<br><br>
`
Insert cell
dotgraph = // add more info to hovered pop up
Plot.plot({
//title: "Hotel Rank Vs Hotel Score",
width: 1000,
height: 800,
x: {
label: "Hotel Rank",
grid: true,
labelFontSize: 12,
titleFontSize: 14,
scale: { zero: false, nice: true }, // Allow non-zero origin and nice scaling

},
y: {
label: "Hotel Score",
grid: true,
reverse: true,
labelFontSize: 12,
titleFontSize: 14,
scale: { zero: false, nice: true },

},

selection: {
hotelSelect: {
type: "single",
fields: ["HotelName"],
bind: "legend",
empty: "none"
}
},
transform: [
{ filter: { selection: "hotelSelect" } }
],


interaction: {
zoom: "wheel!",
pan: true,
},
color: {
legend: true,
type: "linear",
domain: [0, 300],
range: ["#ffcccc", "#990000"],
unknown: "black"
},
marks: [
Plot.dot(thebesthotel, {
x: "Rank",
y: "Score",
stroke: "Rooms",
fillOpacity: 0.7,
strokeWidth: 3,
tip: true
})
]
})

Insert cell
Insert cell
viewof selected_country = country_picker();
Insert cell
country_picker = function () {
const W = width;
const H = (W * height) / width;
const svg = d3
.select(DOM.svg(W, H))
.style("background-color", "lightblue"); // Set the background color to light blue
const g = svg.append("g");
const g_countries = g.append("g");

let x = 0;
let z = 1;

const projection = d3.geoMercator()
.scale(width / (2 * Math.PI)) // Adjust scale
.translate([W / 2, H / 2]); // Center the map


const path = d3.geoPath().projection(projection);
const selection_text = svg.append("text")
.attr("x", 0)
.attr("y", 0)
.attr('dy', "1em")
.style("font-size", "40px") // Set the desired font size here
.text("Select a country")
svg.call(d3.zoom()
.extent([[0, 0], [width, height]])
.scaleExtent([1, 8])
.translateExtent([[0,0], [width, height]])
.on("zoom", zoomed));

// Create a tooltip div and initially hide it
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px");
svg.on("click", function() {
// Hide tooltip
tooltip.style("opacity", 0);
});

// Add circles:
g.selectAll("circles")
.data(coordinates)
.enter()
.append("circle")
.attr("cx", function(d){ return projection([d.Long, d.Lat])[0]})
.attr("cy", function(d){ return projection([d.Long, d.Lat])[1]})
.attr("r", 7)
.style("fill", "69b3a2")
.attr("stroke", "#FF474C")
.attr("stroke-width", 3)
.attr("fill-opacity", .4)
.on("mouseover", function(event, d) {
d3.select(this).attr("r", 10).style("fill", "orange");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 7).style("fill", "#69b3a2");
})
.on("click", function(event, d) {
// Stop event propagation
event.stopPropagation();

// Show tooltip
tooltip.style("opacity", 1)
.html(`Hotel: ${d.Hotel}<br/>
Rank: ${d.Rank}<br/>
Location: ${d.Location}, ${d.Country}<br/>
Theme: ${d.Theme}<br/>
Year built: ${d.Year.toString().replace(/,/g, '')}`)
.style("left", (event.pageX) + "px")
.style("top", (event.pageY - 28) + "px");
// Select the country on the map
g_countries.selectAll("path")
.attr("fill", function(pathData) {
return pathData.properties.name === d.Country ? "orange" : "#eee";
});

// Update the selection text
selection_text.text(d.Country);
});
g_countries
.selectAll("path")
.data(areasUpdated)
.join("path")
.attr("fill", "#eee")
.attr("d", path)
.attr("stroke", "black")
.attr("stroke-linejoin", "round")
.on("mouseover", function(event, d) {
d3.select(this).attr("fill", "lightpink"); // Change color on hover
})
.on("mouseout", function() {
d3.select(this).attr("fill", "#eee"); // Revert color on mouse out
})
.append("title")
.text(d => d.properties.name);
function zoomed({transform}) {
g.attr("transform", transform);

// Scale circle sizes based on zoom level
g.selectAll("circle")
.attr("r", 7 / transform.k); // Adjust the divisor (7 in this case) to control the scaling effect
}

return Object.assign(svg.node(), {
value: null,
onclick: event => {
if (event.target.tagName == 'path') {
// Change countries colors
g_countries.selectAll("path").attr("fill", "#eee");
d3.select(event.target).attr('fill', 'orange');
// Update views' value
event.currentTarget.value = d3.select(event.target).datum().properties;
// Update legend
selection_text.text(event.currentTarget.value.name);
// Dispatch event
event.currentTarget.dispatchEvent(new CustomEvent("input"));
}
if (event.target.tagName == 'circles') {
}
}
});
}

Insert cell
width = 1000

Insert cell
height = 1000
Insert cell
projection = d3
.geoConicConformal()
.parallels([40, 68])
.rotate([-9.05, 0])
.center([9.5 - 9.05, 54.1])
.scale(1690)
.translate([width / 2, height / 2])

Insert cell
path = d3.geoPath().projection(projection)
Insert cell
graticule = d3.geoGraticule10()
Insert cell
areas = topojson.feature(world, world.objects.countries).features; // Adjust 'countries' to the correct object name

Insert cell
areasUpdated = areas.filter(d => d.properties.name !== "Antarctica");

Insert cell
world = FileAttachment("countries-50m.json").json()
Insert cell
Hotels with coordinates - Sheet1.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

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

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