Skip to content

Commit 255a116

Browse files
committed
spruce it up
1 parent 7b70477 commit 255a116

7 files changed

Lines changed: 72 additions & 7 deletions

File tree

index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>titiler-cmr showcase</title>
7+
<link rel="icon" type="image/png" href="/titiler-cmr.png" />
78
</head>
89
<body>
910
<div id="map"></div>
1011

1112
<button id="controls-toggle" aria-label="Toggle controls" aria-expanded="false"></button>
1213
<div id="controls"></div>
1314

15+
<div id="logo">
16+
<img src="/titiler-cmr.png" alt="titiler-cmr" />
17+
</div>
18+
1419
<div id="zoom-guard">Zoom in further to see data</div>
1520

1621
<div id="loading">

public/titiler-cmr.png

233 KB
Loading

src/config.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ export type RenderConfig = {
2626
* Controls the date picker UI and how the `datetime` query param is formed.
2727
* - `single`: one date input; datetime sent as a single-day range.
2828
* - `range`: two date inputs (start / end).
29+
* - `month`: a single month/year picker; datetime sent as the full calendar month range.
2930
*/
3031
export type DateConfig =
3132
| { mode: "single"; default: string }
32-
| { mode: "range"; default: [string, string] };
33+
| { mode: "range"; default: [string, string] }
34+
| { mode: "month"; default: string };
3335

3436
/**
3537
* A numeric range input rendered as two number fields (min, max).
@@ -91,6 +93,8 @@ export type CollectionConfig = {
9193
minzoom: number;
9294
maxzoom: number;
9395
backend: "rasterio" | "xarray";
96+
/** HTML string shown in the MapLibre attribution control for this layer. */
97+
attribution?: string;
9498
/** Controls the date picker UI and the datetime query param format. */
9599
date: DateConfig;
96100
renders: RenderConfig[];
@@ -124,7 +128,8 @@ export const DATASETS: DatasetConfig[] = [
124128
minzoom: 5,
125129
maxzoom: 13,
126130
backend: "rasterio",
127-
date: { mode: "range", default: ["2025-09-01", "2025-09-30"] },
131+
attribution: '<a href="https://lpdaac.usgs.gov/products/hlsl30v002/" target="_blank">HLS Landsat (NASA LP DAAC)</a>',
132+
date: { mode: "month", default: "2025-09" },
128133
queryParams: [
129134
{
130135
type: "range",
@@ -166,7 +171,8 @@ export const DATASETS: DatasetConfig[] = [
166171
backend: "rasterio",
167172
minzoom: 5,
168173
maxzoom: 13,
169-
date: { mode: "range", default: ["2025-09-01", "2025-09-30"] },
174+
attribution: '<a href="https://lpdaac.usgs.gov/products/hlss30v002/" target="_blank">HLS Sentinel-2 (NASA LP DAAC / ESA)</a>',
175+
date: { mode: "month", default: "2025-09" },
170176
queryParams: [
171177
{
172178
type: "range",
@@ -212,6 +218,7 @@ export const DATASETS: DatasetConfig[] = [
212218
backend: "xarray",
213219
minzoom: 6,
214220
maxzoom: 13,
221+
attribution: '<a href="https://nisar.jpl.nasa.gov/" target="_blank">NISAR GCOV (NASA JPL / ASF DAAC)</a>',
215222
date: { mode: "range", default: ["2026-01-01", "2026-02-01"] },
216223
queryParams: [
217224
{
@@ -263,6 +270,7 @@ export const DATASETS: DatasetConfig[] = [
263270
backend: "xarray",
264271
minzoom: 0,
265272
maxzoom: 7,
273+
attribution: '<a href="https://podaac.jpl.nasa.gov/dataset/MUR-JPL-L4-GLOB-v4.1" target="_blank">MUR SST (NASA JPL PO.DAAC)</a>',
266274
date: { mode: "single", default: "2024-01-15" },
267275
renders: [
268276
{

src/controls.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ function toDatetimeRange(startDate: string, endDate: string): string {
2222
return `${startDate}T00:00:00Z/${endDate}T23:59:59Z`;
2323
}
2424

25+
/** Converts a "YYYY-MM" month string into a full-month RFC 3339 datetime range. */
26+
function monthToDatetimeRange(yearMonth: string): string {
27+
const [year, month] = yearMonth.split("-").map(Number);
28+
const lastDay = new Date(year, month, 0).getDate();
29+
const end = `${yearMonth}-${String(lastDay).padStart(2, "0")}`;
30+
return toDatetimeRange(`${yearMonth}-01`, end);
31+
}
32+
2533
function makeLabel(text: string, forId: string): HTMLLabelElement {
2634
const el = document.createElement("label");
2735
el.textContent = text;
@@ -66,6 +74,16 @@ function renderDateControls(
6674
const d = input.value || collection.date.default;
6775
return `${d}T00:00:00Z/${d}T23:59:59Z`;
6876
};
77+
} else if (collection.date.mode === "month") {
78+
const defaultMonth = collection.date.default;
79+
const input = document.createElement("input");
80+
input.type = "month";
81+
input.id = "month-input";
82+
input.value = defaultMonth;
83+
input.addEventListener("change", onChange);
84+
container.appendChild(makeLabel("Month", "month-input"));
85+
container.appendChild(input);
86+
return () => monthToDatetimeRange(input.value || defaultMonth);
6987
} else {
7088
const startInput = makeDateInput("start-date-input");
7189
const endInput = makeDateInput("end-date-input");

src/layers.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,15 @@ function addRasterLayer(
6767
id: string,
6868
tilejsonUrl: string,
6969
minzoom: number,
70-
maxzoom?: number
70+
maxzoom?: number,
71+
attribution?: string
7172
): void {
72-
map.addSource(id, { type: "raster", url: tilejsonUrl, tileSize: 256 });
73+
map.addSource(id, {
74+
type: "raster",
75+
url: tilejsonUrl,
76+
tileSize: 256,
77+
...(attribution ? { attribution } : {}),
78+
});
7379
map.addLayer({
7480
id,
7581
type: "raster",
@@ -96,11 +102,11 @@ export function updateLayer(map: Map, state: ControlState): void {
96102
p.set("minzoom", String(sub.minzoom));
97103
if (sub.maxzoom !== undefined) p.set("maxzoom", String(sub.maxzoom));
98104
for (const [k, v] of Object.entries(sub.params)) p.set(k, v);
99-
addRasterLayer(map, `cmr-${i}`, `${base}?${p}`, sub.minzoom, sub.maxzoom);
105+
addRasterLayer(map, `cmr-${i}`, `${base}?${p}`, sub.minzoom, sub.maxzoom, collection.attribution);
100106
});
101107
} else {
102108
const p = buildBaseParams(collection, render, datetime, extraParams);
103109
p.set("minzoom", String(collection.minzoom));
104-
addRasterLayer(map, "cmr-0", `${base}?${p}`, collection.minzoom);
110+
addRasterLayer(map, "cmr-0", `${base}?${p}`, collection.minzoom, undefined, collection.attribution);
105111
}
106112
}

src/main.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ const { getState } = initControls((state) => {
2828

2929
map.on("load", () => {
3030
map.setProjection({ type: "globe" });
31+
map.setSky({
32+
"sky-color": "#0d1117",
33+
"horizon-color": "#1a2233",
34+
"fog-color": "#0d1117",
35+
"fog-ground-blend": 0.9,
36+
"horizon-fog-blend": 0.5,
37+
"sky-horizon-blend": 0.5,
38+
"atmosphere-blend": 0.3,
39+
});
3140
initLoading(map, () => getState().collection.minzoom);
3241
initZoomGuard(map, () => getState().collection.minzoom);
3342
updateLayer(map, getState());

src/style.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ body {
1919
#map {
2020
position: absolute;
2121
inset: 0;
22+
background: #0d1117;
2223
}
2324

2425
/* Mobile toggle button (hidden on desktop) */
@@ -142,6 +143,7 @@ body {
142143
}
143144

144145
#controls input[type="date"],
146+
#controls input[type="month"],
145147
#controls input[type="text"] {
146148
width: 100%;
147149
background: rgba(255, 255, 255, 0.08);
@@ -155,6 +157,7 @@ body {
155157

156158
#controls select:focus,
157159
#controls input[type="date"]:focus,
160+
#controls input[type="month"]:focus,
158161
#controls input[type="text"]:focus {
159162
outline: 1px solid rgba(100, 160, 255, 0.6);
160163
}
@@ -200,6 +203,22 @@ body {
200203
flex-shrink: 0;
201204
}
202205

206+
/* Logo */
207+
#logo {
208+
position: absolute;
209+
bottom: 36px;
210+
left: 12px;
211+
z-index: 10;
212+
}
213+
214+
#logo img {
215+
display: block;
216+
height: 48px;
217+
width: auto;
218+
border-radius: 6px;
219+
opacity: 0.9;
220+
}
221+
203222
/* Zoom guard */
204223
#zoom-guard {
205224
position: absolute;

0 commit comments

Comments
 (0)