From fea07d2de01cf9eefadb62f9fc6233894fe3039c Mon Sep 17 00:00:00 2001 From: Kieran Farr Date: Tue, 31 Dec 2024 00:11:35 -0800 Subject: [PATCH 1/8] barely working streetplan --- src/components/managed-street.js | 95 ++++++++++++++++++- .../AddLayerPanel/createLayerFunctions.js | 26 +++++ .../components/AddLayerPanel/layersData.js | 12 ++- 3 files changed, 130 insertions(+), 3 deletions(-) diff --git a/src/components/managed-street.js b/src/components/managed-street.js index 32bdcf0ff..73b75cdaf 100644 --- a/src/components/managed-street.js +++ b/src/components/managed-street.js @@ -122,8 +122,7 @@ AFRAME.registerComponent('managed-street', { if (data.sourceType === 'streetmix-url') { this.loadAndParseStreetmixURL(data.sourceValue); } else if (data.sourceType === 'streetplan-url') { - // this function is not yet implemented - this.refreshFromStreetplanURL(data.sourceValue); + this.loadAndParseStreetplanURL(data.sourceValue); } else if (data.sourceType === 'json-blob') { // if data.sourceValue is a string convert string to object for parsing but keep string for saving if (typeof data.sourceValue === 'string') { @@ -274,6 +273,98 @@ AFRAME.registerComponent('managed-street', { }); } }, + loadAndParseStreetplanURL: async function (streetplanURL) { + console.log( + '[managed-street] loader', + 'sourceType: `streetplan-url`, loading from', + streetplanURL + ); + + try { + const response = await fetch(streetplanURL); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const streetplanData = await response.json(); + const boulevard = streetplanData.project['My Street']['Boulevard Alt 1']; + + // Convert StreetPlan format to managed-street format + const streetObject = { + name: streetplanData.project.ProjectName, + width: 0, // Will be calculated from segments + length: + parseFloat(streetplanData.project['My Street'].LengthMiles) * + 5280 * + 0.3048, // Convert miles to meters + segments: [] + }; + + // Process segments + const segments = boulevard.segments; + for (const segmentKey in segments) { + const segment = segments[segmentKey]; + const segmentWidth = parseFloat(segment.width) * 0.3048; // Convert feet to meters + streetObject.width += segmentWidth; + + // Convert segment type based on your schema + let segmentType = 'drive-lane'; // Default type + let segmentDirection = 'inbound'; + let segmentColor = window.STREET.colors.white; + + switch (segment.Type) { + case 'BikesPaths': + segmentType = 'bike-lane'; + break; + case 'Walkways': + segmentType = 'sidewalk'; + break; + case 'Transit': + segmentType = 'bus-lane'; + segmentColor = window.STREET.colors.red; + break; + case 'Median/Buffer': + segmentType = 'divider'; + break; + case 'Curbside': + segmentType = 'divider'; + break; + case 'Gutter': + segmentType = 'parking-lane'; + break; + case 'Furniture': + segmentType = 'sidewalk-tree'; + break; + // Add more type mappings as needed + } + + // Determine direction based on segment data + if (segment.Direction === 'Coming') { + segmentDirection = 'inbound'; + } else if (segment.Direction === 'Going') { + segmentDirection = 'outbound'; + } + + streetObject.segments.push({ + type: segmentType, + width: segmentWidth, + name: segment.title, + level: parseFloat(segment.MaterialH) || 0, + direction: segmentDirection, + color: segmentColor, + surface: segment.Material?.toLowerCase() || 'asphalt' + }); + } + + // Parse the street object + this.parseStreetObject(streetObject); + } catch (error) { + console.error('[managed-street] loader', 'Loading Error:', error); + STREET.notify.warningMessage( + 'Error loading StreetPlan data: ' + error.message + ); + } + }, loadAndParseStreetmixURL: async function (streetmixURL) { const data = this.data; const streetmixAPIURL = streetmixUtils.streetmixUserToAPI(streetmixURL); diff --git a/src/editor/components/components/AddLayerPanel/createLayerFunctions.js b/src/editor/components/components/AddLayerPanel/createLayerFunctions.js index eed592dd6..a163e4d12 100644 --- a/src/editor/components/components/AddLayerPanel/createLayerFunctions.js +++ b/src/editor/components/components/AddLayerPanel/createLayerFunctions.js @@ -77,6 +77,32 @@ export function createManagedStreetFromStreetmixURLPrompt(position) { } } +export function createManagedStreetFromStreetplanURLPrompt(position) { + // This creates a new Managed Street + let streetplanURL = prompt( + 'Please enter a StreetPlan URL', + 'https://streetplan.net/3dstreet/89474' + ); + + if (streetplanURL && streetplanURL !== '') { + const definition = { + id: createUniqueId(), + components: { + position: position ?? '0 0 0', + 'managed-street': { + sourceType: 'streetplan-url', + sourceValue: streetplanURL, + showVehicles: true, + showStriping: true, + synchronize: true + } + } + }; + + AFRAME.INSPECTOR.execute('entitycreate', definition); + } +} + export function createManagedStreetFromStreetObject(position, streetObject) { // This creates a new Managed Street if (streetObject && streetObject !== '') { diff --git a/src/editor/components/components/AddLayerPanel/layersData.js b/src/editor/components/components/AddLayerPanel/layersData.js index c20b2b372..f69e49ba2 100644 --- a/src/editor/components/components/AddLayerPanel/layersData.js +++ b/src/editor/components/components/AddLayerPanel/layersData.js @@ -70,12 +70,22 @@ export const streetLayersData = [ id: 8, handlerFunction: createFunctions.createManagedStreetFromStreetmixURLPrompt }, + { + name: '(Beta) Managed Street from Streetplan URL', + img: '', + requiresPro: true, + icon: '', + description: + 'Create a new street from Streetplan URL using the Managed Street component.', + id: 9, + handlerFunction: createFunctions.createManagedStreetFromStreetplanURLPrompt + }, { name: '(Beta) Managed Street 60ft RoW / 36ft Roadway Width', img: 'ui_assets/cards/street-preset-60-36.jpg', icon: 'ui_assets/cards/icons/3dst24.png', description: 'Premade Street 60ft Right of Way / 36ft Roadway Width', - id: 9, + id: 10, handlerFunction: createFunctions.create60ftRightOfWayManagedStreet } ]; From c581d96624f09fe12b66f7b68c6e757b7542fe45 Mon Sep 17 00:00:00 2001 From: Kieran Farr Date: Tue, 31 Dec 2024 14:22:48 -0800 Subject: [PATCH 2/8] v1 of basic streetplan mapping --- src/components/managed-street.js | 91 +++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/components/managed-street.js b/src/components/managed-street.js index 73b75cdaf..0470e4dcb 100644 --- a/src/components/managed-street.js +++ b/src/components/managed-street.js @@ -5,6 +5,67 @@ const { segmentVariants } = require('../segments-variants.js'); const streetmixUtils = require('../tested/streetmix-utils'); const streetmixParsersTested = require('../tested/aframe-streetmix-parsers-tested'); +// STREETPLAN HELPER FUNCTIONS +// Material mapping from Streetplan to 3DStreet surfaces +const STREETPLAN_MATERIAL_MAPPING = { + 'asphalt black': 'asphalt', + 'asphalt blue': 'asphalt', + 'asphalt red 1': 'asphalt', + 'asphalt red 2': 'asphalt', + 'asphalt green': 'asphalt', + 'asphalt old': 'asphalt', + 'standard concrete': 'concrete', + grass: 'grass', + 'grass dead': 'grass', + 'pavers tan': 'sidewalk', + 'pavers brown': 'sidewalk', + 'pavers mixed': 'sidewalk', + 'pavers red': 'sidewalk', + 'tint conc. or dirt': 'gravel', + dirt: 'gravel', + gravel: 'gravel', + stonetan: 'sidewalk', + 'sidewalk 2': 'sidewalk', + 'cobble stone': 'sidewalk', + 'solid black': 'solid', + 'painted intersection': 'asphalt', + 'grass with edging': 'grass', + xeriscape: 'grass', + 'grassslopemedian 12ft': 'grass', + 'grassslopemedian 24ft': 'grass', + 'grassslope 12ft-left': 'grass', + 'grassslope 12ft-right': 'grass', + 'grassslope 24ft-left': 'grass', + 'grassslope 24ft-right': 'grass', + sand: 'sand' +}; + +const STREETPLAN_OBJECT_MAPPING = { + 'Japanese Zelkova': 'tree3', + 'TallPlantBox (12ft)': 'dividers-bush' + // Add more mappings as needed +}; + +// Helper function to parse O-Tags string into array +function parseOTags(tags) { + if (!tags || tags === '-') return []; + return tags.split('", "').map((t) => t.replace(/"/g, '').trim()); +} + +// Helper function to create clone configuration +function createCloneConfig(name, tags) { + if (!name || name === '-') return null; + + const model = STREETPLAN_OBJECT_MAPPING[name]; + if (!model) return null; + + return { + mode: 'fixed', // default to fixed mode + model: model, + spacing: 15 // default spacing + }; +} + AFRAME.registerComponent('managed-street', { schema: { width: { @@ -304,6 +365,12 @@ AFRAME.registerComponent('managed-street', { const segments = boulevard.segments; for (const segmentKey in segments) { const segment = segments[segmentKey]; + + // Skip Buildings and Setback segments + if (segment.Type === 'Buildings' || segment.Type === 'Setback') { + continue; + } + const segmentWidth = parseFloat(segment.width) * 0.3048; // Convert feet to meters streetObject.width += segmentWidth; @@ -345,6 +412,27 @@ AFRAME.registerComponent('managed-street', { segmentDirection = 'outbound'; } + // Map the material using the STREETPLAN_MATERIAL_MAPPING, fallback to 'asphalt' if not found + const material = segment.Material?.toLowerCase() || ''; + const mappedSurface = + STREETPLAN_MATERIAL_MAPPING[material] || 'asphalt'; + + // Map the O-Tags to clone configurations + const generated = {}; + const clones = []; + // Process O1, O2, O3 configurations + ['O1', 'O2', 'O3'].forEach((prefix) => { + const name = segment[`${prefix}-Name`]; + const tags = parseOTags(segment[`${prefix}-Tags`]); + const cloneConfig = createCloneConfig(name, tags); + if (cloneConfig) { + clones.push(cloneConfig); + } + }); + if (clones.length > 0) { + generated.clones = clones; + } + streetObject.segments.push({ type: segmentType, width: segmentWidth, @@ -352,7 +440,8 @@ AFRAME.registerComponent('managed-street', { level: parseFloat(segment.MaterialH) || 0, direction: segmentDirection, color: segmentColor, - surface: segment.Material?.toLowerCase() || 'asphalt' + surface: mappedSurface, + generated: clones.length > 0 ? generated : undefined }); } From 29c9932c71e81aade09fc0ac110fc223c5a43f2a Mon Sep 17 00:00:00 2001 From: Kieran Farr Date: Tue, 31 Dec 2024 14:35:33 -0800 Subject: [PATCH 3/8] streetplan object mapping --- src/components/managed-street.js | 116 ++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/src/components/managed-street.js b/src/components/managed-street.js index 0470e4dcb..f9b1a6067 100644 --- a/src/components/managed-street.js +++ b/src/components/managed-street.js @@ -41,9 +41,121 @@ const STREETPLAN_MATERIAL_MAPPING = { }; const STREETPLAN_OBJECT_MAPPING = { + 'Away, Left Park, Head In': '', + 'Barrier 1-ft': '', + 'Barrier 2-ft': '', + 'Bike Food Cart': '', + 'BikeRack Bollard': '', + 'Bikelane ShareCar': '', + 'Blank PedRefuge (8ft)': '', + 'Blue Car': '', + 'Blue Mailbox': '', + 'Bollard Plastic Yellow': '', + 'Boxwood planter 2ft': '', + 'Boxwood planter 3ft': '', + 'Boxwood planter 5ft': '', + 'Bur Oak': 'tree3', + 'Cactus Median (10ft)': '', + 'Cactus Median (12ft)': '', + 'Cactus Median (4ft)': '', + 'Cactus Median (6ft)': '', + 'Cactus Median (8ft)': '', + 'DesertWillow Texas': 'tree3', + 'Empty place holder': '', + 'English oak': 'tree3', + 'FleaMarket Stuff': '', + 'Flower Median (10ft)': '', + 'Flower Median (12ft)': '', + 'Flower Median (4ft)': '', + 'Flower Median (6ft)': '', + 'Flower Median (8ft)': '', + 'Flower Pot 4ft': '', + 'FloweringPear 18ft': '', + 'Flowers PedRefuge (8ft)': '', + Goldenraintree: 'tree3', + 'GrassMound (10ft)': '', + 'GrassMound (12ft)': '', + 'GrassMound (4ft)': '', + 'GrassMound (6ft)': '', + 'GrassMound (8ft)': '', + 'Grassy Median (10ft)': '', + 'Grassy Median (12ft)': '', + 'Grassy Median (4ft)': '', + 'Grassy Median (6ft)': '', + 'Grassy Median (8ft)': '', + 'Green Car': '', + 'Historic Light': '', + Honeylocust: '', 'Japanese Zelkova': 'tree3', - 'TallPlantBox (12ft)': 'dividers-bush' - // Add more mappings as needed + 'Japanese lilac': 'tree3', + 'Jerusalem Thorn': 'tree3', + 'Kentucky Coffeetree': 'tree3', + 'Large Food Cart': '', + 'Large Oak': '', + 'Light rail poles': '', + 'Moto highway rider': '', + 'Mountable Barrier 1-ft': '', + 'NYC Bike Rack': '', + 'Orange Barrel': '', + 'Palm Tree': 'palm-tree', + 'PalmTree 20ft': 'palm-tree', + 'PalmTree 28ft': 'palm-tree', + 'Pine Tree': 'tree3', + 'Pink flower 16ft': '', + 'Planter flowers': '', + 'Planter with bench': '', + 'Power Tower 30ft': '', + 'Purpendicular Right side, Blue': '', + 'Purpendicular Right side, Red': '', + 'Purpleleaf plum': '', + 'Red berries 14ft': '', + 'Rock Median (10ft)': '', + 'Rock Median (12ft)': '', + 'Rock Median (4ft)': '', + 'Rock Median (6ft)': '', + 'Rock Median (8ft)': '', + 'Semi Truck': '', + Shelter: '', + 'Shelter Roundroof': '', + 'Sign directory': '', + 'Small Tree': 'tree3', + 'SmartCar 5ft': '', + 'SoundWall (12ft)': '', + 'SoundWall (8ft)': '', + 'SoundWall Plants (12ft)': '', + 'SoundWall Plants (8ft)': '', + 'Street light': '', + 'Streetlight solar': '', + 'Streetlight solar banners 1': '', + 'Streetlight solar banners 2': '', + TallGrass: '', + 'TallPlantBox (10ft)': '', + 'TallPlantBox (12ft)': 'dividers-bush', + 'TallPlantBox (4ft)': '', + 'TallPlantBox (6ft)': '', + 'TallPlantBox (8ft)': '', + 'TallPlantBox PedRef (10ft)': '', + 'TallPlantBox PedRef (12ft)': '', + 'TallPlantBox PedRef (6ft)': '', + 'TallPlantBox PedRef (8ft)': '', + 'Telephone pole': '', + 'Tent BlueWhite': '', + 'Tent Veggie': '', + 'Toward, Right Park, Head In': '', + 'Tropical Median (4ft)': '', + 'Weeds Median (4ft)': '', + 'Weeds Median (6ft)': '', + 'Weeds Median (8ft)': '', + 'White Sedan': '', + 'White Truck': '', + 'White coup': '', + 'Yellow Sedan': '', + 'historic no banner': '', + 'historic with banners': '', + 'historic with flowers 1': '', + 'historic with flowers 2': '', + 'random trashcan': '', + trashcan: '' }; // Helper function to parse O-Tags string into array From b1051b28ad0bd6ba3cdd6cdab3fde3cbbc090219 Mon Sep 17 00:00:00 2001 From: Kieran Farr Date: Wed, 8 Jan 2025 13:00:09 -0800 Subject: [PATCH 4/8] handle null streetlength --- src/components/managed-street.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/managed-street.js b/src/components/managed-street.js index eb836bda8..c47bea94d 100644 --- a/src/components/managed-street.js +++ b/src/components/managed-street.js @@ -510,14 +510,15 @@ AFRAME.registerComponent('managed-street', { const streetplanData = await response.json(); const boulevard = streetplanData.project['My Street']['Boulevard Alt 1']; + const streetLength = + parseFloat(streetplanData.project['My Street'].LengthMiles) * + 5280 * + 0.3048 || 100; // Convert miles to meters // Convert StreetPlan format to managed-street format const streetObject = { name: streetplanData.project.ProjectName, width: 0, // Will be calculated from segments - length: - parseFloat(streetplanData.project['My Street'].LengthMiles) * - 5280 * - 0.3048, // Convert miles to meters + length: streetLength, segments: [] }; From 4f21775fa625356fd12dafde1034672dec12db44 Mon Sep 17 00:00:00 2001 From: Kieran Farr Date: Thu, 9 Jan 2025 15:49:27 -0800 Subject: [PATCH 5/8] add surface color to streetplan mapping --- src/components/managed-street.js | 72 ++++++++++++++++---------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/components/managed-street.js b/src/components/managed-street.js index c47bea94d..5bf90726b 100644 --- a/src/components/managed-street.js +++ b/src/components/managed-street.js @@ -8,36 +8,36 @@ const streetmixParsersTested = require('../tested/aframe-streetmix-parsers-teste // STREETPLAN HELPER FUNCTIONS // Material mapping from Streetplan to 3DStreet surfaces const STREETPLAN_MATERIAL_MAPPING = { - 'asphalt black': 'asphalt', - 'asphalt blue': 'asphalt', - 'asphalt red 1': 'asphalt', - 'asphalt red 2': 'asphalt', - 'asphalt green': 'asphalt', - 'asphalt old': 'asphalt', - 'standard concrete': 'concrete', - grass: 'grass', - 'grass dead': 'grass', - 'pavers tan': 'sidewalk', - 'pavers brown': 'sidewalk', - 'pavers mixed': 'sidewalk', - 'pavers red': 'sidewalk', - 'tint conc. or dirt': 'gravel', - dirt: 'gravel', - gravel: 'gravel', - stonetan: 'sidewalk', - 'sidewalk 2': 'sidewalk', - 'cobble stone': 'sidewalk', - 'solid black': 'solid', - 'painted intersection': 'asphalt', - 'grass with edging': 'grass', - xeriscape: 'grass', - 'grassslopemedian 12ft': 'grass', - 'grassslopemedian 24ft': 'grass', - 'grassslope 12ft-left': 'grass', - 'grassslope 12ft-right': 'grass', - 'grassslope 24ft-left': 'grass', - 'grassslope 24ft-right': 'grass', - sand: 'sand' + 'asphalt black': { surface: 'asphalt', color: '#aaaaaa' }, + 'asphalt blue': { surface: 'asphalt', color: '#aaaaff' }, + 'asphalt red 1': { surface: 'asphalt', color: '#ffaaaa' }, + 'asphalt red 2': { surface: 'asphalt', color: '#ff0000' }, + 'asphalt green': { surface: 'asphalt', color: '#aaffaa' }, + 'asphalt old': { surface: 'asphalt' }, + 'standard concrete': { surface: 'concrete' }, + grass: { surface: 'grass' }, + 'grass dead': { surface: 'grass' }, + 'pavers tan': { surface: 'sidewalk' }, + 'pavers brown': { surface: 'sidewalk' }, + 'pavers mixed': { surface: 'sidewalk' }, + 'pavers red': { surface: 'sidewalk', color: '#ffaaaa' }, + 'tint conc. or dirt': { surface: 'gravel' }, + dirt: { surface: 'gravel' }, + gravel: { surface: 'gravel' }, + stonetan: { surface: 'sidewalk' }, + 'sidewalk 2': { surface: 'sidewalk' }, + 'cobble stone': { surface: 'sidewalk' }, + 'solid black': { surface: 'solid' }, + 'painted intersection': { surface: 'asphalt' }, + 'grass with edging': { surface: 'grass' }, + xeriscape: { surface: 'grass' }, + 'grassslopemedian 12ft': { surface: 'grass' }, + 'grassslopemedian 24ft': { surface: 'grass' }, + 'grassslope 12ft-left': { surface: 'grass' }, + 'grassslope 12ft-right': { surface: 'grass' }, + 'grassslope 24ft-left': { surface: 'grass' }, + 'grassslope 24ft-right': { surface: 'grass' }, + sand: { surface: 'sand' } }; const STREETPLAN_OBJECT_MAPPING = { @@ -522,7 +522,7 @@ AFRAME.registerComponent('managed-street', { segments: [] }; - // Process segments + // Process streetplan segments const segments = boulevard.segments; for (const segmentKey in segments) { const segment = segments[segmentKey]; @@ -535,11 +535,11 @@ AFRAME.registerComponent('managed-street', { const segmentWidth = parseFloat(segment.width) * 0.3048; // Convert feet to meters streetObject.width += segmentWidth; - // Convert segment type based on your schema + // Convert streetplan segment type based on your schema let segmentType = 'drive-lane'; // Default type let segmentDirection = 'inbound'; - let segmentColor = window.STREET.colors.white; + // convert from streetplan type to managed street default type switch (segment.Type) { case 'BikesPaths': segmentType = 'bike-lane'; @@ -549,7 +549,6 @@ AFRAME.registerComponent('managed-street', { break; case 'Transit': segmentType = 'bus-lane'; - segmentColor = window.STREET.colors.red; break; case 'Median/Buffer': segmentType = 'divider'; @@ -576,7 +575,8 @@ AFRAME.registerComponent('managed-street', { // Map the material using the STREETPLAN_MATERIAL_MAPPING, fallback to 'asphalt' if not found const material = segment.Material?.toLowerCase() || ''; const mappedSurface = - STREETPLAN_MATERIAL_MAPPING[material] || 'asphalt'; + STREETPLAN_MATERIAL_MAPPING[material]?.surface || 'asphalt'; + const mappedColor = STREETPLAN_MATERIAL_MAPPING[material]?.color; // Map the O-Tags to clone configurations const generated = {}; @@ -600,7 +600,7 @@ AFRAME.registerComponent('managed-street', { name: segment.title, level: parseFloat(segment.MaterialH) || 0, direction: segmentDirection, - color: segmentColor, + color: mappedColor || window.STREET.types[segmentType]?.color, surface: mappedSurface, generated: clones.length > 0 ? generated : undefined }); From 0fb5a85bc3e31f98af94c2b52f51c28e665fe412 Mon Sep 17 00:00:00 2001 From: Kieran Farr Date: Thu, 9 Jan 2025 15:57:16 -0800 Subject: [PATCH 6/8] map MaterialH > level --- src/components/managed-street.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/managed-street.js b/src/components/managed-street.js index 5bf90726b..53d6e5d6d 100644 --- a/src/components/managed-street.js +++ b/src/components/managed-street.js @@ -598,7 +598,7 @@ AFRAME.registerComponent('managed-street', { type: segmentType, width: segmentWidth, name: segment.title, - level: parseFloat(segment.MaterialH) || 0, + level: parseFloat(segment.MaterialH) === 0.5 ? 1 : 0, direction: segmentDirection, color: mappedColor || window.STREET.types[segmentType]?.color, surface: mappedSurface, From 7b24be5ae518f7f8956b9646ae28badedef4136e Mon Sep 17 00:00:00 2001 From: Kieran Farr Date: Thu, 9 Jan 2025 16:51:12 -0800 Subject: [PATCH 7/8] add additional streetplan mappings --- src/components/managed-street.js | 263 +++++++++++++++++-------------- 1 file changed, 145 insertions(+), 118 deletions(-) diff --git a/src/components/managed-street.js b/src/components/managed-street.js index 53d6e5d6d..ea301d901 100644 --- a/src/components/managed-street.js +++ b/src/components/managed-street.js @@ -41,134 +41,161 @@ const STREETPLAN_MATERIAL_MAPPING = { }; const STREETPLAN_OBJECT_MAPPING = { - 'Away, Left Park, Head In': '', - 'Barrier 1-ft': '', - 'Barrier 2-ft': '', - 'Bike Food Cart': '', - 'BikeRack Bollard': '', - 'Bikelane ShareCar': '', - 'Blank PedRefuge (8ft)': '', - 'Blue Car': '', - 'Blue Mailbox': '', - 'Bollard Plastic Yellow': '', - 'Boxwood planter 2ft': '', - 'Boxwood planter 3ft': '', - 'Boxwood planter 5ft': '', - 'Bur Oak': 'tree3', - 'Cactus Median (10ft)': '', - 'Cactus Median (12ft)': '', - 'Cactus Median (4ft)': '', - 'Cactus Median (6ft)': '', - 'Cactus Median (8ft)': '', - 'DesertWillow Texas': 'tree3', - 'Empty place holder': '', - 'English oak': 'tree3', - 'FleaMarket Stuff': '', - 'Flower Median (10ft)': '', - 'Flower Median (12ft)': '', - 'Flower Median (4ft)': '', - 'Flower Median (6ft)': '', - 'Flower Median (8ft)': '', - 'Flower Pot 4ft': '', - 'FloweringPear 18ft': '', - 'Flowers PedRefuge (8ft)': '', - Goldenraintree: 'tree3', - 'GrassMound (10ft)': '', - 'GrassMound (12ft)': '', - 'GrassMound (4ft)': '', - 'GrassMound (6ft)': '', - 'GrassMound (8ft)': '', - 'Grassy Median (10ft)': '', - 'Grassy Median (12ft)': '', - 'Grassy Median (4ft)': '', - 'Grassy Median (6ft)': '', - 'Grassy Median (8ft)': '', - 'Green Car': '', - 'Historic Light': '', - Honeylocust: '', - 'Japanese Zelkova': 'tree3', - 'Japanese lilac': 'tree3', - 'Jerusalem Thorn': 'tree3', - 'Kentucky Coffeetree': 'tree3', - 'Large Food Cart': '', - 'Large Oak': '', - 'Light rail poles': '', - 'Moto highway rider': '', - 'Mountable Barrier 1-ft': '', - 'NYC Bike Rack': '', - 'Orange Barrel': '', - 'Palm Tree': 'palm-tree', - 'PalmTree 20ft': 'palm-tree', - 'PalmTree 28ft': 'palm-tree', - 'Pine Tree': 'tree3', - 'Pink flower 16ft': '', - 'Planter flowers': '', - 'Planter with bench': '', - 'Power Tower 30ft': '', - 'Purpendicular Right side, Blue': '', - 'Purpendicular Right side, Red': '', - 'Purpleleaf plum': '', - 'Red berries 14ft': '', - 'Rock Median (10ft)': '', - 'Rock Median (12ft)': '', - 'Rock Median (4ft)': '', - 'Rock Median (6ft)': '', - 'Rock Median (8ft)': '', - 'Semi Truck': '', - Shelter: '', - 'Shelter Roundroof': '', - 'Sign directory': '', - 'Small Tree': 'tree3', - 'SmartCar 5ft': '', - 'SoundWall (12ft)': '', - 'SoundWall (8ft)': '', - 'SoundWall Plants (12ft)': '', - 'SoundWall Plants (8ft)': '', - 'Street light': '', - 'Streetlight solar': '', - 'Streetlight solar banners 1': '', - 'Streetlight solar banners 2': '', - TallGrass: '', - 'TallPlantBox (10ft)': '', - 'TallPlantBox (12ft)': 'dividers-bush', - 'TallPlantBox (4ft)': '', - 'TallPlantBox (6ft)': '', - 'TallPlantBox (8ft)': '', - 'TallPlantBox PedRef (10ft)': '', - 'TallPlantBox PedRef (12ft)': '', - 'TallPlantBox PedRef (6ft)': '', - 'TallPlantBox PedRef (8ft)': '', - 'Telephone pole': '', - 'Tent BlueWhite': '', - 'Tent Veggie': '', - 'Toward, Right Park, Head In': '', - 'Tropical Median (4ft)': '', - 'Weeds Median (4ft)': '', - 'Weeds Median (6ft)': '', - 'Weeds Median (8ft)': '', - 'White Sedan': '', - 'White Truck': '', - 'White coup': '', - 'Yellow Sedan': '', - 'historic no banner': '', - 'historic with banners': '', - 'historic with flowers 1': '', - 'historic with flowers 2': '', - 'random trashcan': '', - trashcan: '' + 'away, left park, head in': '', + 'barrier 1-ft': 'temporary-jersey-barrier-concrete', + 'barrier 2-ft': 'temporary-jersey-barrier-concrete', + 'bike food cart': '', + 'bikelane sharecar': '', + 'bikerack bollard': '', + 'blank pedrefuge (8ft)': '', + 'blue car': 'sedan-rig', + 'blue mailbox': 'usps-mailbox', + 'bollard plastic yellow': 'bollard', + boulevardcirculator: 'minibus', + 'boulevardcirculator rev': 'minibus', + 'boxwood planter 2ft': 'dividers-planter-box', + 'boxwood planter 3ft': 'dividers-planter-box', + 'boxwood planter 5ft': 'dividers-planter-box', + 'bur oak': 'tree3', + bus: 'bus', + 'bus rev': 'bus', + 'cactus median (10ft)': 'dividers-bush', + 'cactus median (12ft)': 'dividers-bush', + 'cactus median (4ft)': 'dividers-bush', + 'cactus median (6ft)': 'dividers-bush', + 'cactus median (8ft)': 'dividers-bush', + 'casual woman': '', + couple: '', + 'couple biking': '', + 'desertwillow texas': 'tree3', + 'dog walker': '', + 'empty place holder': '', + 'english oak': 'tree3', + 'fleamarket stuff': '', + 'flower median (10ft)': 'dividers-flowers', + 'flower median (12ft)': 'dividers-flowers', + 'flower median (4ft)': 'dividers-flowers', + 'flower median (6ft)': 'dividers-flowers', + 'flower median (8ft)': 'dividers-flowers', + 'flower pot 4ft': 'dividers-flowers', + 'floweringpear 18ft': 'tree3', + 'flowers pedrefuge (8ft)': 'dividers-flowers', + goldenraintree: 'tree3', + 'golfcart red 4ft back': 'tuk-tuk', + 'grassmound (10ft)': '', + 'grassmound (12ft)': '', + 'grassmound (4ft)': '', + 'grassmound (6ft)': '', + 'grassmound (8ft)': '', + 'grassy median (10ft)': '', + 'grassy median (12ft)': '', + 'grassy median (4ft)': '', + 'grassy median (6ft)': '', + 'grassy median (8ft)': '', + 'green car': 'sedan-rig', + 'heavy rail': 'tram', + 'heavy rail rev': 'tram', + 'historic light': 'lamp-traditional', + 'historic no banner': 'lamp-traditional', + 'historic with banners': 'lamp-traditional', + 'historic with flowers 1': 'lamp-traditional', + 'historic with flowers 2': 'lamp-traditional', + honeylocust: 'tree3', + 'japanese lilac': 'tree3', + 'japanese zelkova': 'tree3', + 'jerusalem thorn': 'tree3', + 'kentucky coffeetree': 'tree3', + 'large food cart': '', + 'large oak': 'tree3', + 'light rail poles': '', + 'moto highway rider': 'motorbike', + 'mountable barrier 1-ft': '', + 'nev shuttle back': 'minibus', + 'nev shuttle front': 'minibus', + 'nyc bike rack': 'bikerack', + 'orange barrel': 'temporary-traffic-cone', + 'palm tree': 'palm-tree', + 'palmtree 20ft': 'palm-tree', + 'palmtree 28ft': 'palm-tree', + 'pine tree': 'tree3', + 'pink flower 16ft': 'tree3', + 'planter flowers': 'dividers-flowers', + 'planter with bench': 'bench', + 'polaris gem e4': 'tuk-tuk', + 'power tower 30ft': '', + 'purpendicular right side, blue': '', + 'purpendicular right side, red': '', + 'purpleleaf plum': 'tree3', + 'random trashcan': 'trash-bin', + 'red berries 14ft': 'tree3', + 'red car': 'sedan-rig', + 'red jeep': 'suv-rig', + 'rock median (10ft)': '', + 'rock median (12ft)': '', + 'rock median (4ft)': '', + 'rock median (6ft)': '', + 'rock median (8ft)': '', + 'semi truck': 'box-truck-rig', + 'serious man': '', + shelter: 'bus-stop', + 'shelter roundroof': 'bus-stop', + 'sign directory': 'wayfinding', + 'silver suv': 'suv-rig', + 'small tree': 'tree3', + smallnev: 'minibus', + 'smartcar 5ft': 'self-driving-cruise-car-rig', + 'soundwall (12ft)': '', + 'soundwall (8ft)': '', + 'soundwall plants (12ft)': '', + 'soundwall plants (8ft)': '', + 'street light': 'lamp-modern', + 'streetcar blue': 'trolley', + 'streetcar red 1': 'trolley', + 'streetcar red 2': 'trolley', + 'streetcar yellow': 'trolley', + 'streetlight solar': 'lamp-modern', + 'streetlight solar banners 1': 'lamp-modern', + 'streetlight solar banners 2': 'lamp-modern', + tallgrass: '', + 'tallplantbox (10ft)': '', + 'tallplantbox (12ft)': 'dividers-bush', + 'tallplantbox (4ft)': '', + 'tallplantbox (6ft)': '', + 'tallplantbox (8ft)': '', + 'tallplantbox pedref (10ft)': '', + 'tallplantbox pedref (12ft)': '', + 'tallplantbox pedref (6ft)': '', + 'tallplantbox pedref (8ft)': '', + 'telephone pole': 'utility_pole', + 'tent bluewhite': '', + 'tent veggie': '', + 'toward, right park, head in': '', + trashcan: 'trash-bin', + 'tropical median (4ft)': 'palm-tree', + 'two bikes back': '', + 'uta bus': 'bus', + 'uta lightrail': 'tram', + 'uta lightrail rev': 'tram', + 'weeds median (4ft)': '', + 'weeds median (6ft)': '', + 'weeds median (8ft)': '', + 'white coup': 'sedan-rig', + 'white sedan': 'sedan-rig', + 'white truck': 'box-truck-rig', + 'yellow sedan': 'sedan-rig' }; -// Helper function to parse O-Tags string into array +// Streetplan Helper function to parse O-Tags string into array function parseOTags(tags) { if (!tags || tags === '-') return []; return tags.split('", "').map((t) => t.replace(/"/g, '').trim()); } -// Helper function to create clone configuration +// Streetplan Helper function to create clone configuration function createCloneConfig(name, tags) { if (!name || name === '-') return null; - const model = STREETPLAN_OBJECT_MAPPING[name]; + const model = STREETPLAN_OBJECT_MAPPING[name.toLowerCase()]; if (!model) return null; return { From 506474b769d5981b8a16677cb6b074cad2c6d6b8 Mon Sep 17 00:00:00 2001 From: Kieran Farr Date: Mon, 13 Jan 2025 14:17:37 -0800 Subject: [PATCH 8/8] use new streetplan loader by default remove old streetplan loader --- src/components/streetplan-loader.js | 140 ----------- src/index.js | 1 - src/json-utils_1.1.js | 27 ++- src/streetplan/conversion-map.js | 347 ---------------------------- src/streetplan/streetplan-utils.js | 87 ------- 5 files changed, 20 insertions(+), 582 deletions(-) delete mode 100644 src/components/streetplan-loader.js delete mode 100644 src/streetplan/conversion-map.js delete mode 100644 src/streetplan/streetplan-utils.js diff --git a/src/components/streetplan-loader.js b/src/components/streetplan-loader.js deleted file mode 100644 index 8954d3447..000000000 --- a/src/components/streetplan-loader.js +++ /dev/null @@ -1,140 +0,0 @@ -/* global AFRAME, XMLHttpRequest */ -import useStore from '../store.js'; -var streetplanUtils = require('../streetplan/streetplan-utils.js'); - -const state = useStore.getState(); - -AFRAME.registerComponent('streetplan-loader', { - dependencies: ['street'], - schema: { - streetplanStreetURL: { type: 'string' }, - streetplanAPIURL: { type: 'string' }, - streetplanEncJSON: { type: 'string' }, - showBuildings: { default: true }, - name: { default: '' }, - synchronize: { default: false } - }, - streetplanResponseParse: function (streetplanResponseObject) { - const el = this.el; - const data = this.data; - if (!streetplanResponseObject || !streetplanResponseObject.project) { - console.error('[streetplan-loader] Invalid streetplan data structure'); - return; - } - try { - // convert Streetplan structure to Streetmix-like structure - const streetData = streetplanUtils.convertStreetStruct( - streetplanResponseObject - ); - - const streetplanSegments = streetData.segments; - const streetplanName = streetData.name; - // const projectName = streetData.projectName || streetplanName; - - // Update layer name with project and street names - el.setAttribute('data-layer-name', `StreetPlan • ${streetplanName}`); - - if (!state.sceneTitle) { - state.setSceneTitle(streetplanName); - } - - // Handle buildings if enabled - if (data.showBuildings) { - // Find building segments in the full data - const buildingSegments = streetplanSegments.filter( - (segment) => segment.type === 'Buildings' - ); - - // Set building variants based on side - const leftBuilding = buildingSegments.find((b) => b.side === 'left'); - const rightBuilding = buildingSegments.find((b) => b.side === 'right'); - - if (leftBuilding) { - el.setAttribute('street', 'left', leftBuilding.title); - } - if (rightBuilding) { - el.setAttribute('street', 'right', rightBuilding.title); - } - } - // Set street type - el.setAttribute('street', 'type', 'streetmixSegmentsMetric'); - - // Filter out building segments for the main street data if needed - const finalSegments = data.showBuildings - ? streetplanSegments.filter((s) => s.type !== 'Buildings') - : streetplanSegments; - - // Set JSON attribute last - el.setAttribute( - 'street', - 'JSON', - JSON.stringify({ streetmixSegmentsMetric: finalSegments }) - ); - el.emit('streetplan-loader-street-loaded'); - } catch (error) { - console.error('[streetplan-loader] Error parsing street data:', error); - el.emit('streetplan-loader-error', { error }); - } - }, - init: function () { - this.el.setAttribute('streetplan-loader', 'synchronize', true); - }, - update: function (oldData) { - const data = this.data; - - // Skip update if synchronization is disabled - if (!data.synchronize) return; - - // Handle URL encoded JSON data - if (data.streetplanEncJSON) { - try { - const streetplanJSON = decodeURIComponent(data.streetplanEncJSON); - this.streetplanResponseParse(JSON.parse(streetplanJSON)); - } catch (error) { - console.error('[streetplan-loader] Error parsing encoded JSON:', error); - this.el.emit('streetplan-loader-error', { error }); - } - return; - } - - // Skip if URLs haven't changed - if ( - oldData.streetplanStreetURL === data.streetplanStreetURL && - oldData.streetplanAPIURL === data.streetplanAPIURL - ) { - return; - } - - // Load from API - const request = new XMLHttpRequest(); - console.log('[streetplan-loader]', 'GET ' + data.streetplanAPIURL); - - request.open('GET', data.streetplanAPIURL, true); - request.onload = () => { - if (request.status >= 200 && request.status < 400) { - try { - const streetplanResponseObject = JSON.parse(request.response); - this.streetplanResponseParse(streetplanResponseObject); - this.el.setAttribute('streetplan-loader', 'synchronize', false); - } catch (error) { - console.error( - '[streetplan-loader] Error parsing API response:', - error - ); - this.el.emit('streetplan-loader-error', { error }); - } - } else { - const error = new Error(`Server returned status ${request.status}`); - console.error('[streetplan-loader] API request failed:', error); - this.el.emit('streetplan-loader-error', { error }); - } - }; - - request.onerror = () => { - const error = new Error('Network request failed'); - console.error('[streetplan-loader] Network error:', error); - this.el.emit('streetplan-loader-error', { error }); - }; - request.send(); - } -}); diff --git a/src/index.js b/src/index.js index b223b2adf..ae18771af 100644 --- a/src/index.js +++ b/src/index.js @@ -16,7 +16,6 @@ require('./components/notify.js'); require('./components/create-from-json'); require('./components/screentock.js'); require('aframe-atlas-uvs-component'); -require('./components/streetplan-loader'); require('./components/street-geo.js'); require('./components/street-environment.js'); require('./components/intersection.js'); diff --git a/src/json-utils_1.1.js b/src/json-utils_1.1.js index dac6e2cba..91bb719c9 100644 --- a/src/json-utils_1.1.js +++ b/src/json-utils_1.1.js @@ -1,4 +1,5 @@ import useStore from './store'; +import { createUniqueId } from './editor/lib/entity'; /* global AFRAME, Node */ /* version: 1.0 */ @@ -529,18 +530,30 @@ AFRAME.registerComponent('set-loader-from-hash', { streetURL ); } else if (streetURL.includes('streetplan.net/')) { - // load from Streetplan encoded JSON in URL + // instead, load streetplan via managed street the new addlayerpanel console.log( '[set-loader-from-hash]', - 'Set streetplan-loader streetplanAPIURL to', + 'Create new Managed Street with StreetPlan URL', streetURL ); + if (streetURL && streetURL !== '') { + const definition = { + id: createUniqueId(), + components: { + 'managed-street': { + sourceType: 'streetplan-url', + sourceValue: streetURL, + showVehicles: true, + showStriping: true, + synchronize: true + } + } + }; - this.el.setAttribute( - 'streetplan-loader', - 'streetplanAPIURL', - streetURL - ); + setTimeout(() => { + AFRAME.INSPECTOR.execute('entitycreate', definition); + }, 1000); + } } else { // try to load JSON file from remote resource console.log( diff --git a/src/streetplan/conversion-map.js b/src/streetplan/conversion-map.js deleted file mode 100644 index 82db4ded5..000000000 --- a/src/streetplan/conversion-map.js +++ /dev/null @@ -1,347 +0,0 @@ -// conversion map StreetPan -> Streetmix sidewalk segment types mapping -/* -StreetPlanType1: - { - StreetPlanSubtype: StreetmixType, - --- or --- - StreetPlanSubtype: { - "tag": StreetPlanTag, - "type": StreetmixType, - "variantString": Streetmix VariantString, can be formed based on other Streetplan parameters - (Name or Tag) or be constant, like: 'sidewalk', - - "variantStringAdd": get parameter values from this list and generate variantString. - Often variantString looks like this: 'outbound|regular|road' - example for bike-path. - variantStringAdd will be: 'direction|material|variantString', - - "nameToVariantMap": mapping rule StreetPlan O1-Name -> VariantString, - "tagToVariantMap": mapping rule StreetPlan O1-Tags -> VariantString, - "names": names (StreetPlan O1-Name) for this Streetmix Segment type - }, - --- or --- - // for one (O1-Tags) there can be different streetmix segment types, - // which are determined by the name (O1-Name) - StreetPlanSubtype: [ - different options of tags (O1-Tags) and streetMix data for each - ] - } -*/ -const mapping = { - Setback: { - '': { type: 'sidewalk', variantString: 'empty' }, - Trees: { type: 'sidewalk-tree', variantString: 'big' }, - tree: { type: 'divider', variantString: 'palm-tree' }, - Benchs: { type: 'sidewalk-bench', variantStringAdd: 'side' } - }, - Walkways: { - '': { type: 'sidewalk', variantString: 'empty' }, - Trees: { type: 'sidewalk-tree', variantString: 'big' }, - pedestrian: { type: 'sidewalk', variantString: 'dense' }, - Benchs: { type: 'sidewalk-bench', variantStringAdd: 'side' }, - Tables: { type: 'outdoor-dining', variantString: 'occupied|sidewalk' } - }, - Furniture: { - '': { type: 'sidewalk', variantString: 'empty' }, - Trees: { type: 'sidewalk-tree', variantString: 'big' }, - season_tree: { type: 'sidewalk-tree', variantString: 'big' }, - Shelters: { - type: 'transit-shelter', - variantString: 'street-level', - variantStringAdd: 'side|variantString' - }, - Pedestrian: { type: 'sidewalk', variantString: 'dense' } - }, - Curbside: { - '': { type: 'sidewalk', variantString: 'empty' }, - Lights: { - type: 'sidewalk-lamp', - tagToVariantMap: { - 'Historic Lights': 'traditional', - 'Regular Lights': 'modern' - }, - variantStringAdd: 'side|variantString' - }, - Poles: { type: 'utilities', variantStringAdd: 'side' }, - BikeRacks: { - type: 'sidewalk-bike-rack', - nameToVariantMap: { - 'Sideview Modern': 'sidewalk-parallel', - Sideview: 'sidewalk-parallel', - 'NYC Bike Rack': 'sidewalk' - }, - variantStringAdd: 'side|variantString' - } - }, - BikesPaths: { - '': { type: 'bike-lane', variantString: 'sidewalk' }, - Bikes: { - type: 'bike-lane', - variantString: 'sidewalk', - variantStringAdd: 'direction|material|variantString' - } - }, - Gutter: { - '': { type: 'divider', variantString: 'median' }, - Gutter: { type: 'divider', variantString: 'median' } - }, - Transit: { - '': { - tag: 'Bus Vehicles', - type: 'bus-lane', - variantString: 'typical', - variantStringAdd: 'direction|material|variantString' - }, - Transit: [ - { - tag: 'Rail Vehicles', - type: 'streetcar', - names: [ - 'StreetCar Yellow', - 'StreetCar Blue', - 'StreetCar Red 1', - 'StreetCar Red 2' - ], - variantStringAdd: 'direction|material' - }, - { - tag: 'Rail Vehicles', - type: 'light-rail', - names: ['UTA LightRail'], - variantStringAdd: 'direction|material' - }, - // there are only reversed light rail vehicles in Streetplan - { - tag: 'Rail Vehicles Reversed', - type: 'light-rail', - variantStringAdd: 'direction|material' - }, - { - tag: 'Bus Vehicles', - type: 'bus-lane', - variantString: 'typical', - variantStringAdd: 'direction|material|variantString' - } - ] - }, - Cars: { - '': { - type: 'drive-lane', - variantString: 'car', - variantStringAdd: 'direction|variantString' - }, - Autos: { - type: 'drive-lane', - variantString: 'car', - variantStringAdd: 'direction|variantString' - }, - Truck: { - type: 'drive-lane', - variantString: 'truck', - variantStringAdd: 'direction|variantString' - } - }, - Parking: { - '': { - tag: 'Parking - Parallel', - type: 'parking-lane', - variantStringAdd: 'direction|side' - }, - Parallel: { - tag: 'Parking - Parallel', - type: 'parking-lane', - variantStringAdd: 'direction|side' - }, - AngleNormal: { - tag: 'Parking - Angle', - type: 'parking-lane', - nameToVariantMap: { - 'Away, L. Park, Head In': 'angled-rear-left', - 'Toward, R. Park, Head In': 'angled-front-right', - 'Toward, L. Park, Head In': 'angled-front-left', - 'Away, R. Park, Head In': 'angled-rear-right' - }, - variantStringAdd: 'side' - }, - Perpendicular: { - type: 'parking-lane', - variantString: 'sideways', - variantStringAdd: 'variantString|side' - } - }, - Buffers: { - '': { type: 'divider', variantString: 'median' }, - Trees: { type: 'divider', variantString: 'big-tree' }, - tree: { type: 'divider', variantString: 'palm-tree' }, - season_tree: { type: 'divider', variantString: 'big-tree' }, - median: { type: 'divider', variantString: 'planting-strip' }, - planter: { type: 'divider', variantString: 'planting-strip' } - } -}; -// copy repeating rules -mapping['Buffers']['AngleNormal'] = mapping['Parking']['AngleNormal']; -mapping['Buffers']['Autos'] = mapping['Cars']['Autos']; -mapping['Buffers']['Purpendicular'] = mapping['Parking']['Perpendicular']; -mapping['Median/Buffer'] = mapping['Buffers']; -mapping['Setback']['tree'] = mapping['Buffers']['tree']; -mapping['Setback']['Trees'] = mapping['Buffers']['Trees']; -mapping['Setback']['season_tree'] = mapping['Buffers']['season_tree']; -// fix for typo Purpendicular -mapping['Parking']['Purpendicular'] = mapping['Parking']['Perpendicular']; -mapping['Setback']['Purpendicular'] = mapping['Parking']['Perpendicular']; -mapping['Setback']['AngleNormal'] = mapping['Parking']['AngleNormal']; -mapping['Setback']['planter'] = mapping['Buffers']['planter']; -mapping['Setback']['BikeRacks'] = mapping['Curbside']['BikeRacks']; -mapping['Setback']['Tables'] = mapping['Walkways']['Tables']; -mapping['Setback']['Poles'] = mapping['Curbside']['Poles']; - -mapping['Curbside']['Shelters'] = mapping['Furniture']['Shelters']; -mapping['Curbside']['Benchs'] = mapping['Walkways']['Benchs']; - -mapping['Furniture']['planter'] = mapping['Buffers']['planter']; -mapping['Furniture']['Benchs'] = mapping['Walkways']['Benchs']; -mapping['Furniture']['BikeRacks'] = mapping['Curbside']['BikeRacks']; -mapping['Furniture']['Tables'] = mapping['Walkways']['Tables']; - -const directionMap = { - Coming: 'inbound', - Going: 'outbound', - // make default outbound direction for both variant - Both: 'both', - NA: '' -}; - -const materialMap = { - 'Asphalt Black': 'regular', - 'Asphalt Blue': 'blue', - 'Asphalt Red 1': 'red', - 'Asphalt Red 2': 'red', - 'Asphalt Green': 'green', - 'Asphalt Old': 'regular', - Grass: 'grass', - 'Grass Dead': 'grass' -}; - -// StreetMix variantString often has additional parameters via |, for example: taxi|outbound|right -// generate a streetMix like variantString from the listed parameters in variantStringAdd -function generateVariantString(variantStringKeys, streetmixData) { - const variantString = variantStringKeys - .split('|') - .map((currKey) => streetmixData[currKey]) - .join('|'); - return variantString; -} - -function getDataFromSubtypeMap(convertRule, streetmixData, streetplanData) { - if (typeof convertRule === 'string') { - // convertRule is a Streetmix type. - // Later will add another options for this case - streetmixData['type'] = convertRule; - } else if (Array.isArray(convertRule)) { - // in this case, different segment subtype options - // are associated with the different Streetmix types - - // find the desired Streetmix segment data from the array by Streetplan tag and names(?) - const variantData = convertRule.find((element) => { - const tagValMatches = element['tag'] === streetplanData['O1-Tags']; - if (tagValMatches && element['names']) { - return element['names'].includes(streetplanData['O1-Name']); - } - return tagValMatches; - }); - - streetmixData['variantString'] = ''; - - const variantString = variantData['variantString']; - if (variantString && typeof variantString === 'string') { - streetmixData['variantString'] = variantString; - } - - // generate a streetMix like variantString from the listed parameter values - streetmixData['type'] = variantData['type']; - const variantStringKeys = variantData['variantStringAdd']; - if (variantStringKeys) { - streetmixData['variantString'] = generateVariantString( - variantStringKeys, - streetmixData - ); - } - } else if (typeof convertRule === 'object') { - // in this case, different variants of the segment subtype - // are associated with different variantString of the Streetmix segment - - streetmixData['type'] = convertRule['type']; - streetmixData['variantString'] = ''; - - const variantString = convertRule['variantString']; - if (variantString && typeof variantString === 'string') { - streetmixData['variantString'] = variantString; - } - - // get variantString from {"O1-Name" (StreetPlan Object Name) : variantString} mapping data - const nameToVariantMap = convertRule['nameToVariantMap']; - if (nameToVariantMap && nameToVariantMap[streetplanData['O1-Name']]) { - streetmixData['variantString'] = - nameToVariantMap[streetplanData['O1-Name']]; - } - - // get variantString from {"O1-Tags" (StreetPlan Tag) : variantString} mapping data - const tagToVariantMap = convertRule['tagToVariantMap']; - if (tagToVariantMap && tagToVariantMap[streetplanData['O1-Tags']]) { - streetmixData['variantString'] = - tagToVariantMap[streetplanData['O1-Tags']]; - } - - // generate a streetMix like variantString from the listed parameter values - const variantStringKeys = convertRule['variantStringAdd']; - if (variantStringKeys) { - streetmixData['variantString'] = generateVariantString( - variantStringKeys, - streetmixData - ); - } - } - - return streetmixData; -} - -// convert streetPlan segment data to Streetmix segment data -function convertSegment(data) { - let streetmixData = {}; - const streetplanType = data['Type']; - const streetplanSubtype = data['Subtype']; - // mapping rule for current Streetplan subtypes - const subtypeMap = mapping[streetplanType]; - - // convert elevation value to Streetmix format: 0, 1, 2 - streetmixData['elevation'] = data['MaterialH'] / 0.5; - streetmixData['width'] = data['width']; - streetmixData['direction'] = directionMap[data['Direction']]; - if (data['side']) { - streetmixData['side'] = data['side']; - } - if (data['Material']) { - streetmixData['material'] = materialMap[data['Material']]; - } - - if (subtypeMap) { - const convertRule = subtypeMap[streetplanSubtype]; - if (convertRule) { - streetmixData = getDataFromSubtypeMap(convertRule, streetmixData, data); - } else { - streetmixData['type'] = streetplanType; - // STREET.notify.warningMessage(`The '${streetplanSubtype}' subtype of StreetPlan segment '${segmentType}' is not yet supported in 3DStreet`); - console.log( - `The '${streetplanSubtype}' subtype of StreetPlan segment '${streetplanType}' is not yet supported in 3DStreet` - ); - } - } else { - streetmixData['type'] = streetplanType; - // STREET.notify.warningMessage(`The '${streetplanType}' StreetPlan segment type is not yet supported in 3DStreet`); - console.log( - `The '${streetplanType}' StreetPlan segment type is not yet supported in 3DStreet` - ); - } - return streetmixData; -} - -module.exports.convertSegment = convertSegment; diff --git a/src/streetplan/streetplan-utils.js b/src/streetplan/streetplan-utils.js deleted file mode 100644 index 5d91f46af..000000000 --- a/src/streetplan/streetplan-utils.js +++ /dev/null @@ -1,87 +0,0 @@ -// utils for StreetPlan parsing -const mappingUtils = require('./conversion-map.js'); - -/** - * Convert width from feet to meters - * @param {Object} streetData - Street data containing segments - */ -function convertStreetValues(streetData) { - streetData.segments.forEach((segmentData) => { - segmentData.width *= 0.3048; - }); -} - -/** - * Convert street structure to match Streetmix JSON Schema - * @param {Object} projectData - Full project data from StreetPlan - * @returns {Object} Converted street structure - */ -function convertStreetStruct(projectData) { - // Validate input - if (!projectData || !projectData.project) { - throw new Error('Invalid project data structure'); - } - - const newStruct = { - projectName: projectData.project.ProjectName || 'Unnamed Project', - units: projectData.project.DistanceUnits || 'Feet' - }; - - // Find the first street in the project (excluding metadata keys) - const streets = Object.keys(projectData.project).filter( - (key) => key !== 'ProjectName' && key !== 'DistanceUnits' - ); - - if (streets.length === 0) { - throw new Error('No streets found in project'); - } - - const streetName = streets[0]; - newStruct.name = streetName; - - // Get the street variations (e.g. "Boulevard Alt 1", "Existing Conditions") - const variations = Object.keys(projectData.project[streetName]).filter( - (key) => key !== 'LengthMiles' - ); - - // Use the first variation by default - const selectedVariation = variations[0]; - newStruct.altName = selectedVariation; - newStruct.lengthMiles = projectData.project[streetName].LengthMiles; - - // Get segments from the selected variation - const streetData = projectData.project[streetName][selectedVariation]; - - // Remove segment indexes and convert to array - newStruct.segments = Object.values(streetData.segments); - - // Convert measurements if needed - convertStreetValues(newStruct); - - // Remove buildings and setback segments, convert remaining data - newStruct.segments = convertSegmentData(newStruct.segments).filter( - (segmentData) => { - return !['Buildings', 'setback'].includes(segmentData['type']); - } - ); - - // Add new metadata fields if present - newStruct.segments = newStruct.segments.map((segment) => { - if (segment.Group1) segment.group1 = segment.Group1; - if (segment.Group2) segment.group2 = segment.Group2; - if (segment.Cost) segment.cost = segment.Cost; - return segment; - }); - - return newStruct; -} - -function convertSegmentData(segments) { - return segments.map(mappingUtils.convertSegment); -} - -module.exports = { - convertStreetStruct, - convertSegmentData, - convertStreetValues -};