Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/modules/ui/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ outlineLayers.on("change", function () {
styleHeightmapScheme.on("change", function () {
getEl().attr("scheme", this.value);
drawHeightmap();
window.updateLegendIfVisible?.();
});

openCreateHeightmapSchemeButton.on("click", function () {
Expand Down
114 changes: 108 additions & 6 deletions public/modules/ui/units-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function editUnits() {
ensureEl("distanceScaleInput").on("change", changeDistanceScale);
ensureEl("heightUnit").on("change", changeHeightUnit);
ensureEl("heightExponentInput").on("input", changeHeightExponent);
ensureEl("altitudeLegend").on("click", toggleLegend);
ensureEl("temperatureScale").on("change", changeTemperatureScale);

ensureEl("populationRateInput").on("change", changePopulationRate);
Expand Down Expand Up @@ -56,17 +57,21 @@ function editUnits() {
}

function changeHeightUnit() {
if (this.value !== "custom_name") return;

prompt("Provide a custom name for a height unit", {default: ""}, custom => {
this.options.add(new Option(custom, custom, false, true));
lock("heightUnit");
});
if (this.value === "custom_name") {
prompt("Provide a custom name for a height unit", {default: ""}, custom => {
this.options.add(new Option(custom, custom, false, true));
lock("heightUnit");
updateLegendIfVisible();
});
return;
}
updateLegendIfVisible();
}

function changeHeightExponent() {
calculateTemperatures();
if (layerIsOn("toggleTemperature")) drawTemperature();
updateLegendIfVisible();
}
Comment on lines 71 to 75
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added on: 6e7a5b8.
updateLegendIfVisible


function changeTemperatureScale() {
Expand Down Expand Up @@ -117,7 +122,104 @@ function editUnits() {
localStorage.removeItem("populationRate");
localStorage.removeItem("urbanization");
localStorage.removeItem("urbanDensity");

updateLegendIfVisible();
}

function toggleLegend() {
const isVisible = legend.selectAll("*").size() > 0;

if (isVisible) {
clearLegend();
} else {
updateAndDisplayLegend();
}
}

let legendHeightsCache = null;

function getLegendHeightsCache() {
const heights = pack?.cells?.h;
if (!heights) return null;

if (
legendHeightsCache &&
legendHeightsCache.heightsRef === heights &&
legendHeightsCache.heightsLen === heights.length
) {
return legendHeightsCache;
}

const countByHeight = new Map();
for (const h of heights) countByHeight.set(h, (countByHeight.get(h) || 0) + 1);

const sortedHeights = Array.from(countByHeight.keys()).sort((a, b) => a - b);

// Select a representative sample of heights across the range
const totalSamples = 10;
const step = Math.max(1, Math.floor(sortedHeights.length / totalSamples));
const sampledHeights = sortedHeights.filter(
(_, index) => index % step === 0 || index === sortedHeights.length - 1
);

legendHeightsCache = {
heightsRef: heights,
heightsLen: heights.length,
countByHeight,
sortedHeights,
sampledHeights
};

return legendHeightsCache;
}

function updateAndDisplayLegend() {
const cache = getLegendHeightsCache();
if (!cache) return;

const schemeName =
terrs.select("#landHeights").attr("scheme") ||
terrs.select("#oceanHeights").attr("scheme") ||
"bright";
const scheme = getColorScheme(schemeName);

const heightUnitSelect = ensureEl("heightUnit");
const selectedOpt = heightUnitSelect.selectedOptions[0];
const selectedText = selectedOpt?.text?.trim() ?? "";
const parenAbbrev = selectedText.match(/\(([^)]+)\)/)?.[1];
const heightUnitName =
heightUnitSelect.value === "custom_name"
? heightUnitSelect.nextElementSibling?.value || selectedText
: (parenAbbrev ?? selectedText) || heightUnitSelect.value;

const sampled = cache.sampledHeights.map(height => {
const v = 1 - (height < 20 ? height - 5 : height) / 100;
const sRGB = scheme(v);
return {height, color: sRGB};
});

const data = sampled.map(c => [rn(c.height, 0), c.color, getHeight(c.height)]);

// Set the number of items per column
styleLegendColItems.value = data.length;

drawLegend(`Heights (in ${heightUnitName})`, data);

Comment on lines +203 to +207
// Center the legend label
const legendLabel = legend.select("#legendLabel");
const bbox = legend.node().getBBox();
legendLabel.attr("x", bbox.width / 2);

// Use shared legend positioning logic (defaults near bottom-right when data-x/y are unset)
if (window.fitLegendBox) fitLegendBox();
}

function updateLegendIfVisible() {
if (legend.selectAll("*").size() > 0) {
updateAndDisplayLegend();
}
}
window.updateLegendIfVisible = updateLegendIfVisible;

function addRuler() {
if (!layerIsOn("toggleRulers")) toggleRulers();
Expand Down
5 changes: 5 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5340,6 +5340,11 @@
</slider-input>
</div>

<div data-tip="Toggle altitude legend. Granularity affects the amount of heights shown">
<span>Height legend:</span>
<button id="altitudeLegend" data-tip="Click to show the altitude legend" class="icon-list-bullet"></button>
</div>

<div class="unitsHeader" data-tip="Select Temperature scale">
<span class="icon-temperature-high"></span>
<label>Temperature:</label>
Expand Down
Loading