diff --git a/src/main.js b/src/main.js index 9abd1e6..33373b0 100644 --- a/src/main.js +++ b/src/main.js @@ -5,6 +5,7 @@ const TILE_MAX_NATIVE_ZOOM = 19; const MAP_MAX_ZOOM = 24; const MAP_ZOOM_STEP = 0.25; const WHEEL_PIXELS_PER_ZOOM_LEVEL = 240; +const FEET_PER_METER = 3.280839895; const TIMELINE_PRESETS = [ { id: "purchase", @@ -330,7 +331,8 @@ function buildPopupContent(properties) { const title = `${escapeHtml(name)}`; const subtitle = name === label ? "" : `
${escapeHtml(label)}
`; const allEntries = [...lifecycleEntries, ...entries, ...relationshipEntries]; - if (allEntries.length === 0 && linkEntries.length === 0) { + const measurementTable = buildMeasurementTable(details?.measurements); + if (allEntries.length === 0 && linkEntries.length === 0 && !measurementTable) { return `${title}${subtitle}`; } @@ -341,12 +343,16 @@ function buildPopupContent(properties) { .map((link) => `
Info
`) .join(""); - return `${title}${subtitle}
${detailRows}${linkRows}
`; + return `${title}${subtitle}
${detailRows}${measurementTable}${linkRows}
`; } function shouldShowDetailField(key, details = {}) { + if (key === "measurements") { + return false; + } + if ( - Array.isArray(details.measurements) && + hasDatedMeasurements(details.measurements) && (key === "canopyDiameter" || key === "height") ) { return false; @@ -382,12 +388,8 @@ function formatDetailLabel(key) { } function formatDetailValue(key, value) { - if (key === "measurements") { - return formatPlantMeasurements(value); - } - if (key === "canopyDiameter" || key === "height") { - return `${value} m`; + return formatMetersAsFeet(value); } if (key === "offsetFeet" || key === "spacingFeet") { @@ -397,32 +399,63 @@ function formatDetailValue(key, value) { return String(value); } -function formatPlantMeasurements(measurements) { - if (!Array.isArray(measurements)) { - return String(measurements); - } - - return measurements.map(formatPlantMeasurement).join("; "); +function formatMetersAsFeet(value) { + return `${formatNumber(value * FEET_PER_METER)} ft`; } -function formatPlantMeasurement(measurement) { - const parts = []; +function formatNumber(value) { + return Number(value.toFixed(1)).toString(); +} - if (measurement.measuredDate) { - parts.push(formatDetailValue("measuredDate", measurement.measuredDate)); +function buildMeasurementTable(measurements) { + const datedMeasurements = getDatedMeasurements(measurements); + if (!datedMeasurements.length) { + return ""; } - if (measurement.canopyDiameter !== undefined) { - parts.push( - `canopy ${formatDetailValue("canopyDiameter", measurement.canopyDiameter)}`, - ); + const rows = datedMeasurements + .map( + (measurement) => ` + + ${escapeHtml(measurement.measuredDate)} + ${escapeHtml(formatOptionalMeasurement(measurement.canopyDiameter))} + ${escapeHtml(formatOptionalMeasurement(measurement.height))} + + `, + ) + .join(""); + + return ` +
+
${escapeHtml(formatDetailLabel("measurements"))}:
+ + + + + + + + + ${rows} +
DateCanopy dia.Height
+
+ `; +} + +function formatOptionalMeasurement(value) { + return value === undefined ? "" : formatMetersAsFeet(value); +} + +function hasDatedMeasurements(measurements) { + return getDatedMeasurements(measurements).length > 0; +} + +function getDatedMeasurements(measurements) { + if (!Array.isArray(measurements)) { + return []; } - if (measurement.height !== undefined) { - parts.push(`height ${formatDetailValue("height", measurement.height)}`); - } - - return parts.join(", "); + return measurements.filter((measurement) => measurement.measuredDate); } function escapeHtml(value) { diff --git a/styles.css b/styles.css index 0bdf2fd..bf8917d 100644 --- a/styles.css +++ b/styles.css @@ -129,6 +129,33 @@ main { font-family: "Iowan Old Style", "Palatino Linotype", "URW Palladio L", serif; } +.measurement-table-block { + margin-top: 0.35rem; +} + +.measurement-table { + width: 100%; + margin-top: 0.15rem; + border-collapse: collapse; + font-size: 0.92em; +} + +.measurement-table th, +.measurement-table td { + padding: 0.12rem 0.35rem 0.12rem 0; + border-bottom: 1px solid rgba(87, 68, 38, 0.18); + text-align: left; + white-space: nowrap; +} + +.measurement-table th { + font-weight: 700; +} + +.measurement-table tbody tr:last-child td { + border-bottom: 0; +} + .timeline-control { display: grid; gap: 0.45rem;