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) => ``)
.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"))}:
+
+
+
+ | Date |
+ Canopy dia. |
+ Height |
+
+
+ ${rows}
+
+
+ `;
+}
+
+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;