Skip to content
Merged
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
8 changes: 5 additions & 3 deletions src/CampusContributionStudio.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
MAP_HEIGHT,
MAP_WIDTH,
campusFromQuery,
canonicalBuildingCodeForFeature,
canonicalBuildingsForCampus,
canonicalEntrancesForCampus,
canonicalFootprintsForCampus,
Expand Down Expand Up @@ -832,15 +833,16 @@ export default function CampusContributionStudio() {

<g className="campus-canonical-footprints">
{canonicalFootprints.map((feature, index) => {
const code = feature.properties.buildingCode?.toUpperCase();
const code = canonicalBuildingCodeForFeature(feature);
if (!code) return null;
const key = canonicalBuildingKey(code);
const selected = selectedBuildingKey === key;
return (
<path
key={`${code}-${index}`}
d={geometryPath(feature.geometry, project)}
className={selected ? 'selected' : ''}
onClick={tool === 'entrance' && !selected ? (event) => {
onClick={tool === 'entrance' && !selectedBuilding && !selected ? (event) => {
event.stopPropagation();
selectBuilding(key);
} : undefined}
Expand All @@ -858,7 +860,7 @@ export default function CampusContributionStudio() {
key={item.id}
d={geometryPath(item.geometry, project)}
className={selected ? 'selected' : ''}
onClick={tool === 'entrance' && !selected ? (event) => {
onClick={tool === 'entrance' && !selectedBuilding && !selected ? (event) => {
event.stopPropagation();
selectBuilding(key);
} : undefined}
Expand Down
30 changes: 8 additions & 22 deletions src/campus-contribution-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ import brockCampus from "../universities/brock/campus.json";
import ubcCampus from "../universities/ubc/campus.json";
import waterlooCampus from "../universities/waterloo/campus.json";
import mcgillCampus from "../universities/mcgill/campus.json";
import {
canonicalBuildingCodeForFeature,
universityBuildingFootprint,
} from "./canonical-building-identity.js";

export { canonicalBuildingCodeForFeature } from "./canonical-building-identity.js";

const universityCampusModules = import.meta.glob(
"../universities/**/campus.json",
Expand Down Expand Up @@ -161,32 +167,12 @@ const TRI_CAMPUS_FOOTPRINTS = {
function universityFootprints(dataset) {
return (dataset?.buildings || [])
.filter((b) => b.geometry)
.map((b) => ({
type: "Feature",
id: b.id,
properties: {
buildingId: b.id,
code: b.nativeCodes?.[0] || b.id,
name: b.name,
},
geometry: b.geometry,
}));
.map(universityBuildingFootprint);
}

function universityBuildings(dataset, campusId) {
return (dataset?.buildings || []).map((building) => {
const feature = building.geometry
? {
type: "Feature",
id: building.id,
properties: {
buildingId: building.id,
code: building.nativeCodes?.[0] || building.id,
name: building.name,
},
geometry: building.geometry,
}
: null;
const feature = universityBuildingFootprint(building);
const features = feature ? [feature] : [];
const code = building.nativeCodes?.[0] || building.id.toUpperCase();
const entrances = (dataset.entrances || []).filter(
Expand Down
24 changes: 24 additions & 0 deletions src/canonical-building-identity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export function universityBuildingFootprint(building) {
if (!building?.geometry) return null;
const buildingCode = building.nativeCodes?.[0] || String(building.id).toUpperCase();
return {
type: "Feature",
id: building.id,
properties: {
buildingId: building.id,
buildingCode,
code: building.nativeCodes?.[0] || building.id,
name: building.name,
},
geometry: building.geometry,
};
}

export function canonicalBuildingCodeForFeature(feature) {
const value =
feature?.properties?.buildingCode ??
feature?.properties?.code ??
feature?.properties?.buildingId ??
feature?.id;
return value == null ? null : String(value).trim().toUpperCase() || null;
}
62 changes: 62 additions & 0 deletions tests/campus-contribution-selection.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import assert from "node:assert/strict";
import test from "node:test";

import { readFile } from "node:fs/promises";

import {
canonicalBuildingCodeForFeature,
universityBuildingFootprint,
} from "../src/canonical-building-identity.js";

for (const campusId of ["carleton", "queens", "harvard"]) {
test(`${campusId} footprints retain one selectable canonical building identity`, () => {
return readFile(
new URL(`../universities/${campusId}/campus.json`, import.meta.url),
"utf8",
).then((raw) => {
const buildings = JSON.parse(raw).buildings;
const footprints = buildings
.map(universityBuildingFootprint)
.filter(Boolean);
const selectableCodes = new Set(
buildings.map((building) =>
(
building.nativeCodes?.[0] || building.id.toUpperCase()
).toUpperCase(),
),
);
const footprintCodes = footprints.map(canonicalBuildingCodeForFeature);

assert.ok(
buildings.length > 1,
`${campusId} should expose multiple selectable buildings`,
);
assert.ok(
footprints.length > 1,
`${campusId} should expose multiple mapped footprints`,
);
assert.equal(
footprintCodes.includes(null),
false,
`${campusId} footprints need stable IDs`,
);
assert.ok(
new Set(footprintCodes).size > 1,
`${campusId} footprints must not collapse to one shared selection key`,
);
for (const code of footprintCodes) {
assert.ok(
selectableCodes.has(code),
`${campusId} footprint ${code} must select a building`,
);
}
});
});
}

test("U of T buildingCode identities remain compatible", () => {
assert.equal(
canonicalBuildingCodeForFeature({ properties: { buildingCode: "ba" } }),
"BA",
);
});
Loading