diff --git a/aframe-inspector b/aframe-inspector index c4141c9..8ffa439 160000 --- a/aframe-inspector +++ b/aframe-inspector @@ -1 +1 @@ -Subproject commit c4141c9df701751049494c030915ce562e8ddeb5 +Subproject commit 8ffa4392a72a97e3f04f7fd3af1d0e44599a4053 diff --git a/sample_data/waypoints_graph.json b/sample_data/waypoints_graph.json index 69f2325..30b6ec0 100644 --- a/sample_data/waypoints_graph.json +++ b/sample_data/waypoints_graph.json @@ -4,6 +4,7 @@ "type": "node", "id": "ArenaLab", "description": "", + "tags": {}, "lat": -4.25773, "ele:local": -1, "lon": 4.87359 @@ -11,13 +12,17 @@ { "type": "way", "id": "ArenaLab-ArenaLabDoor", - "nodes": ["ArenaLab", "ArenaLabDoor"], + "nodes": [ + "ArenaLab", + "ArenaLabDoor" + ], "tags": {} }, { "type": "node", "id": "ArenaLabDoor", "description": "", + "tags": {}, "lat": -4.84196, "ele:local": -0.92885, "lon": 1.73607 @@ -25,19 +30,26 @@ { "type": "way", "id": "ArenaLabDoor-ConferenceRoom", - "nodes": ["ArenaLabDoor", "ConferenceRoom"], + "nodes": [ + "ArenaLabDoor", + "ConferenceRoom" + ], "tags": {} }, { "type": "way", "id": "ArenaLabDoor-Lobby2300", - "nodes": ["ArenaLabDoor", "Lobby2300"], + "nodes": [ + "ArenaLabDoor", + "Lobby2300" + ], "tags": {} }, { "type": "node", "id": "ConferenceRoom", "description": "", + "tags": {}, "lat": -3.40554, "ele:local": -1, "lon": 0.78139 @@ -45,13 +57,17 @@ { "type": "way", "id": "ConferenceRoom-Hallway", - "nodes": ["ConferenceRoom", "Hallway"], + "nodes": [ + "ConferenceRoom", + "Hallway" + ], "tags": {} }, { "type": "node", "id": "Entrance2300", "description": "", + "tags": {}, "lat": -7.10102, "ele:local": -1, "lon": -4.61792 @@ -59,13 +75,17 @@ { "type": "way", "id": "Entrance2300-Lobby2300", - "nodes": ["Entrance2300", "Lobby2300"], + "nodes": [ + "Entrance2300", + "Lobby2300" + ], "tags": {} }, { "type": "node", "id": "Hallway", "description": "", + "tags": {}, "lat": -0.56477, "ele:local": -1, "lon": 0.81094 @@ -73,49 +93,71 @@ { "type": "way", "id": "Hallway-StaffOffice", - "nodes": ["Hallway", "StaffOffice"], + "nodes": [ + "Hallway", + "StaffOffice" + ], "tags": {} }, { "type": "way", "id": "Hallway-StudentI", - "nodes": ["Hallway", "StudentI"], + "nodes": [ + "Hallway", + "StudentI" + ], "tags": {} }, { "type": "way", "id": "Hallway-StudentJ", - "nodes": ["Hallway", "StudentJ"], + "nodes": [ + "Hallway", + "StudentJ" + ], "tags": {} }, { "type": "way", "id": "Hallway-StudentN", - "nodes": ["Hallway", "StudentN"], + "nodes": [ + "Hallway", + "StudentN" + ], "tags": {} }, { "type": "way", "id": "Hallway-StudentM", - "nodes": ["Hallway", "StudentM"], + "nodes": [ + "Hallway", + "StudentM" + ], "tags": {} }, { "type": "way", "id": "Hallway-StudentL", - "nodes": ["Hallway", "StudentL"], + "nodes": [ + "Hallway", + "StudentL" + ], "tags": {} }, { "type": "way", "id": "Hallway-StudentK", - "nodes": ["Hallway", "StudentK"], + "nodes": [ + "Hallway", + "StudentK" + ], "tags": {} }, { "type": "node", "id": "Lobby2300", "description": "", + "tags": {}, "lat": -6.04847, "ele:local": -1, "lon": -2.36367 @@ -124,6 +166,7 @@ "type": "node", "id": "Professor", "description": "", + "tags": {}, "lat": 2.60499, "ele:local": -1, "lon": -5.28736 @@ -131,61 +174,89 @@ { "type": "way", "id": "Professor-StudentA", - "nodes": ["Professor", "StudentA"], + "nodes": [ + "Professor", + "StudentA" + ], "tags": {} }, { "type": "way", "id": "Professor-StudentB", - "nodes": ["Professor", "StudentB"], + "nodes": [ + "Professor", + "StudentB" + ], "tags": {} }, { "type": "way", "id": "Professor-StudentC", - "nodes": ["Professor", "StudentC"], + "nodes": [ + "Professor", + "StudentC" + ], "tags": {} }, { "type": "way", "id": "Professor-StudentD", - "nodes": ["Professor", "StudentD"], + "nodes": [ + "Professor", + "StudentD" + ], "tags": {} }, { "type": "way", "id": "Professor-StudentE", - "nodes": ["Professor", "StudentE"], + "nodes": [ + "Professor", + "StudentE" + ], "tags": {} }, { "type": "way", "id": "Professor-StudentF", - "nodes": ["Professor", "StudentF"], + "nodes": [ + "Professor", + "StudentF" + ], "tags": {} }, { "type": "way", "id": "Professor-StudentG", - "nodes": ["Professor", "StudentG"], + "nodes": [ + "Professor", + "StudentG" + ], "tags": {} }, { "type": "way", "id": "Professor-StaffOffice", - "nodes": ["Professor", "StaffOffice"], + "nodes": [ + "Professor", + "StaffOffice" + ], "tags": {} }, { "type": "way", "id": "Professor-StudentH", - "nodes": ["Professor", "StudentH"], + "nodes": [ + "Professor", + "StudentH" + ], "tags": {} }, { "type": "node", "id": "StaffOffice", "description": "", + "tags": {}, "lat": 2.61439, "ele:local": -1, "lon": 0.41135 @@ -194,6 +265,7 @@ "type": "node", "id": "StudentA", "description": "", + "tags": {}, "lat": -4.31889, "ele:local": -1, "lon": -6.55324 @@ -202,6 +274,7 @@ "type": "node", "id": "StudentB", "description": "", + "tags": {}, "lat": -2.68955, "ele:local": -1, "lon": -6.48687 @@ -210,6 +283,7 @@ "type": "node", "id": "StudentC", "description": "", + "tags": {}, "lat": -1.17725, "ele:local": -1, "lon": -6.3836 @@ -218,6 +292,7 @@ "type": "node", "id": "StudentD", "description": "", + "tags": {}, "lat": 0.43108, "ele:local": -1, "lon": -6.44152 @@ -226,6 +301,7 @@ "type": "node", "id": "StudentE", "description": "", + "tags": {}, "lat": -4.16253, "ele:local": -1, "lon": -5.2185 @@ -234,6 +310,7 @@ "type": "node", "id": "StudentF", "description": "", + "tags": {}, "lat": -2.81188, "ele:local": -1, "lon": -5.28351 @@ -242,6 +319,7 @@ "type": "node", "id": "StudentG", "description": "", + "tags": {}, "lat": -1.19974, "ele:local": -1, "lon": -5.21431 @@ -250,6 +328,7 @@ "type": "node", "id": "StudentH", "description": "", + "tags": {}, "lat": 0.50184, "ele:local": -1, "lon": -5.15294 @@ -258,6 +337,7 @@ "type": "node", "id": "StudentI", "description": "", + "tags": {}, "lat": -0.11917, "ele:local": -1, "lon": -2.35952 @@ -266,6 +346,7 @@ "type": "node", "id": "StudentJ", "description": "", + "tags": {}, "lat": -0.42932, "ele:local": -1, "lon": -0.68392 @@ -274,6 +355,7 @@ "type": "node", "id": "StudentK", "description": "", + "tags": {}, "lat": -0.34771, "ele:local": -1, "lon": 2.35552 @@ -282,6 +364,7 @@ "type": "node", "id": "StudentL", "description": "", + "tags": {}, "lat": -0.23317, "ele:local": -1, "lon": 4.03259 @@ -290,6 +373,7 @@ "type": "node", "id": "StudentM", "description": "", + "tags": {}, "lat": -0.29684, "ele:local": -1, "lon": 5.41319 @@ -298,9 +382,10 @@ "type": "node", "id": "StudentN", "description": "", + "tags": {}, "lat": -0.13389, "ele:local": -1, "lon": 7.42173 } ] -} +} \ No newline at end of file diff --git a/waypoint-tagger/package.json b/waypoint-tagger/package.json index cc83121..ccdd079 100644 --- a/waypoint-tagger/package.json +++ b/waypoint-tagger/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build:inspector": "cd ../aframe-inspector && npm run dist:min && cp dist/aframe-inspector.min.js ../waypoint-tagger/dist/dependencies", + "build:inspector": "npm --prefix ../aframe-inspector run dist:max; cp ../aframe-inspector/dist/aframe-inspector.js ./dist/dependencies/", "build": "tsc && vite build", "preview": "vite preview", "deploy": "tsc && vite build && gh-pages -d dist" diff --git a/waypoint-tagger/src/button-handlers/clearWaypoints.ts b/waypoint-tagger/src/button-handlers/clearWaypoints.ts new file mode 100644 index 0000000..d3a03bc --- /dev/null +++ b/waypoint-tagger/src/button-handlers/clearWaypoints.ts @@ -0,0 +1,15 @@ +/** + * removes all nodes with way_point or waypoint_connection components from the + * screen. + * + * we'll call this before loading a new waypoint files, so they don't overlap. + */ + +const clearWaypoints = () => { + document.querySelectorAll("a-entity[way_point]").forEach((e) => e.remove()); + document + .querySelectorAll("a-entity[waypoint_connection]") + .forEach((e) => e.remove()); +}; + +export { clearWaypoints }; diff --git a/waypoint-tagger/src/button-handlers/downloadWaypointsAsJSON.ts b/waypoint-tagger/src/button-handlers/downloadWaypointsAsJSON.ts index 2460cbf..d9d4d89 100644 --- a/waypoint-tagger/src/button-handlers/downloadWaypointsAsJSON.ts +++ b/waypoint-tagger/src/button-handlers/downloadWaypointsAsJSON.ts @@ -48,15 +48,17 @@ function downloadWaypointsAsJSON() { y: number; z: number; description: string; + tags: object; neighbors: Array; // array of ids } >(); for (const wp of waypointEntities) { const id = wp.getAttribute("id"); // TODO: this should be numeric! const { x, y, z } = wp.object3D.position; - const description = wp.components.way_point.data.description; - const neighbors = wp.components.way_point.data.neighbors.split(","); - waypointEntitiesMap.set(id, { id, x, y, z, description, neighbors }); + const description = wp.components.way_point.data.description ?? ""; + const neighbors = wp.components.way_point.data.neighbors.split(",") ?? []; + const tags = wp.components.way_point.data.tags ?? {}; + waypointEntitiesMap.set(id, { id, x, y, z, description, tags, neighbors }); } // initialize the array of output elements @@ -65,13 +67,14 @@ function downloadWaypointsAsJSON() { // iterate through nodes in _increasing_ order of id for (const [ _, - { id, x, y, z, description, neighbors }, + { id, x, y, z, description, tags, neighbors }, ] of waypointEntitiesMap) { // add way_point as 'node' const waypointNode = { type: "node", id, description, + tags, lat: x, "ele:local": y, lon: z, diff --git a/waypoint-tagger/src/button-handlers/loadWaypoints.ts b/waypoint-tagger/src/button-handlers/loadWaypoints.ts index 443b622..d23db7b 100644 --- a/waypoint-tagger/src/button-handlers/loadWaypoints.ts +++ b/waypoint-tagger/src/button-handlers/loadWaypoints.ts @@ -1,6 +1,8 @@ import type { Entity } from "aframe"; import { ElementArraySchema, type Element } from "../schema"; +import { clearWaypoints } from "./clearWaypoints"; +/* loads either from a CSV or JSON, depending on the extension */ function loadWaypointsFromFile(fileSelectEvent: Event): void { const input = fileSelectEvent.target; if ( @@ -11,18 +13,22 @@ function loadWaypointsFromFile(fileSelectEvent: Event): void { ) throw new Error("file select target is not defined"); - const file = input.files![0]; - const [extension] = file.name.split(".").slice(-1); + const [file] = input.files!; + const extension = file.name.split(".").slice(-1)[0].toLowerCase(); - switch (extension.toLowerCase()) { + if (!["csv", "json"].includes(extension)) + throw new Error(`unsupported file extension: .${extension}`); + + // clear existing waypoints before loading new ones + clearWaypoints(); + + switch (extension) { case "csv": loadWaypointsFromCSVFile(file); return; case "json": loadWaypointsFromJSONFile(file); return; - default: - throw new Error(`unsupported file extension: .${extension}`); } } @@ -97,6 +103,7 @@ function createWaypointEntitiesFromCSV( entity.setAttribute("way_point", { ID: row.id, description: row.description || "", // assuming description might be an empty string if not provided + tags: row.tags, }); entity.setAttribute("id", row.id); @@ -127,12 +134,12 @@ function createWaypointEntitiesFromJSON(elements: Element[]) { // first loop: create entities for nodes for (const e of elements) { if (e.type !== "node") continue; - const { lat: x, "ele:local": y, lon: z, id, description } = e; + const { lat: x, "ele:local": y, lon: z, id, description, tags } = e; const entity = document.createElement("a-entity"); entity.setAttribute("position", `${x} ${y} ${z}`); entity.setAttribute("id", id); - entity.setAttribute("way_point", { ID: id, description }); + entity.setAttribute("way_point", { ID: id, description, tags }); // set the gltf-model attribute to the waypoint model entity.setAttribute("gltf-model", "#waypoint_model"); diff --git a/waypoint-tagger/src/components/waypoint.ts b/waypoint-tagger/src/components/waypoint.ts index bee6c5c..a2640b0 100644 --- a/waypoint-tagger/src/components/waypoint.ts +++ b/waypoint-tagger/src/components/waypoint.ts @@ -4,6 +4,11 @@ AFRAME.registerComponent("way_point", { schema: { ID: { type: "string" }, description: { type: "string" }, + tags: { + default: {}, + stringify: (value: object) => + Object.entries(value).map(([key, value]) => `${key}=${value}`), + }, neighbors: { type: "string" }, }, diff --git a/waypoint-tagger/src/components/waypointConnection.ts b/waypoint-tagger/src/components/waypointConnection.ts index 2839df5..fa8c605 100644 --- a/waypoint-tagger/src/components/waypointConnection.ts +++ b/waypoint-tagger/src/components/waypointConnection.ts @@ -1,6 +1,4 @@ -import type { LineBasicMaterial } from "three"; -import type { BufferGeometry } from "three"; -import type { Line } from "three"; +import type { BufferGeometry, Line, LineBasicMaterial } from "three"; AFRAME.registerComponent("waypoint_connection", { schema: { diff --git a/waypoint-tagger/src/schema/index.ts b/waypoint-tagger/src/schema/index.ts index 54a7de4..feeea74 100644 --- a/waypoint-tagger/src/schema/index.ts +++ b/waypoint-tagger/src/schema/index.ts @@ -7,6 +7,7 @@ export const NodeSchema = z.object({ lat: z.number(), // x "ele:local": z.number(), // y lon: z.number(), // z + tags: z.object(), }); export type Node = z.infer;