Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d839573d96 | |||
| 50c7118bff | |||
| d21033cd85 | |||
| 6240ec724f | |||
| 3248c5ad14 | |||
| 917c7ee8a6 | |||
| 17c1f1c3f2 | |||
| 3bcc8c34d6 | |||
| e9834636e6 |
+7
-1
@@ -34,7 +34,12 @@
|
||||
]
|
||||
},
|
||||
"values": {
|
||||
"for": { "in": "objectData" }
|
||||
"for": { "in": "objectData" },
|
||||
"dataReductionAlgorithm": {
|
||||
"top": {
|
||||
"count": 30000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,6 +157,7 @@
|
||||
"https://*.speckle.xyz",
|
||||
"https://latest.speckle.dev",
|
||||
"https://*.speckle.dev",
|
||||
"https://analytics.speckle.systems",
|
||||
"*"
|
||||
]
|
||||
},
|
||||
|
||||
Generated
+50
@@ -11,11 +11,15 @@
|
||||
"@babel/runtime-corejs2": "7.6.0",
|
||||
"@speckle/viewer": "^2.7.1",
|
||||
"core-js": "3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"powerbi-visuals-api": "~4.7.0",
|
||||
"powerbi-visuals-utils-dataviewutils": "2.4.1",
|
||||
"regenerator-runtime": "^0.13.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/core-js": "^2.5.5",
|
||||
"@types/lodash": "^4.14.188",
|
||||
"@types/regenerator-runtime": "^0.13.1",
|
||||
"ts-loader": "6.1.0",
|
||||
"tslint": "^5.18.0",
|
||||
"tslint-microsoft-contrib": "^6.2.0",
|
||||
@@ -446,6 +450,24 @@
|
||||
"node": "^16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/core-js": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/core-js/-/core-js-2.5.5.tgz",
|
||||
"integrity": "sha512-C4vwOHrhsvxn7UFyk4NDQNUpgNKdWsT/bL39UWyD75KSEOObZSKa9mYDOCM5FGeJG2qtbG0XiEbUKND2+j0WOg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.188",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.188.tgz",
|
||||
"integrity": "sha512-zmEmF5OIM3rb7SbLCFYoQhO4dGt2FRM9AMkxvA3LaADOF1n8in/zGJlWji9fmafLoNyz+FoL6FE0SLtGIArD7w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/regenerator-runtime": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/regenerator-runtime/-/regenerator-runtime-0.13.1.tgz",
|
||||
"integrity": "sha512-Wr4Kopo+zs7kl1mxveVrP7Hl5nEzauQKdSNFN5Eg27Ze11MAgJYKgidYc9AAkQzGXXWH9lqVzPbXUGH6M8VY6g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
@@ -904,6 +926,11 @@
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
@@ -1673,6 +1700,24 @@
|
||||
"three": "^0.140.0"
|
||||
}
|
||||
},
|
||||
"@types/core-js": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/core-js/-/core-js-2.5.5.tgz",
|
||||
"integrity": "sha512-C4vwOHrhsvxn7UFyk4NDQNUpgNKdWsT/bL39UWyD75KSEOObZSKa9mYDOCM5FGeJG2qtbG0XiEbUKND2+j0WOg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.188",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.188.tgz",
|
||||
"integrity": "sha512-zmEmF5OIM3rb7SbLCFYoQhO4dGt2FRM9AMkxvA3LaADOF1n8in/zGJlWji9fmafLoNyz+FoL6FE0SLtGIArD7w==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/regenerator-runtime": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/regenerator-runtime/-/regenerator-runtime-0.13.1.tgz",
|
||||
"integrity": "sha512-Wr4Kopo+zs7kl1mxveVrP7Hl5nEzauQKdSNFN5Eg27Ze11MAgJYKgidYc9AAkQzGXXWH9lqVzPbXUGH6M8VY6g==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
@@ -2011,6 +2056,11 @@
|
||||
"json5": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
|
||||
+5
-1
@@ -15,13 +15,17 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.6.0",
|
||||
"@babel/runtime-corejs2": "7.6.0",
|
||||
"@speckle/viewer": "^2.7.1",
|
||||
"@speckle/viewer": "2.7.1",
|
||||
"core-js": "3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"powerbi-visuals-api": "~4.7.0",
|
||||
"powerbi-visuals-utils-dataviewutils": "2.4.1",
|
||||
"regenerator-runtime": "^0.13.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/core-js": "^2.5.5",
|
||||
"@types/lodash": "^4.14.188",
|
||||
"@types/regenerator-runtime": "^0.13.1",
|
||||
"ts-loader": "6.1.0",
|
||||
"tslint": "^5.18.0",
|
||||
"tslint-microsoft-contrib": "^6.2.0",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
"displayName": "Speckle PowerBI Viewer",
|
||||
"guid": "powerbiSpeckleVisualAA98F06515D847E8ACB33BAB487244E0",
|
||||
"visualClassName": "Visual",
|
||||
"version": "2.0.0-alpha1",
|
||||
"version": "2.0.0-alpha3",
|
||||
"description": "An interactive 3D viewer for Speckle Data",
|
||||
"supportUrl": "https://speckle.community",
|
||||
"gitHubUrl": "https://github.com/specklesystems/speckle-powerbi-visuals"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
const TRACK_URL = "https://analytics.speckle.systems/track?ip=1"
|
||||
const MIXPANEL_TOKEN = "acd87c5a50b56df91a795e999812a3a4"
|
||||
const HOST_APP_NAME = "powerbi-visual"
|
||||
|
||||
export enum Event {
|
||||
Create = "Create",
|
||||
Reload = "Reload",
|
||||
Settings = "Settings"
|
||||
}
|
||||
|
||||
export enum SettingsChangedType {
|
||||
Gradient = "Gradient",
|
||||
DefaultCamera = "DefaultCamera",
|
||||
OrthoMode = "OrthoMode"
|
||||
}
|
||||
|
||||
export class Tracker {
|
||||
public static async track(event: Event, properties: any = {}) {
|
||||
return this.trackEvents([
|
||||
{
|
||||
event,
|
||||
properties
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
private static async trackEvents(
|
||||
events: Array<{ event: Event; properties: any }>
|
||||
) {
|
||||
try {
|
||||
var res = await fetch(TRACK_URL, {
|
||||
method: "POST",
|
||||
body:
|
||||
"data=" +
|
||||
JSON.stringify(
|
||||
events.map(e => {
|
||||
Object.assign(e.properties, {
|
||||
token: MIXPANEL_TOKEN,
|
||||
hostApp: HOST_APP_NAME
|
||||
})
|
||||
return e
|
||||
})
|
||||
)
|
||||
})
|
||||
//console.log("Create track", res, await res.json())
|
||||
} catch (e) {
|
||||
console.error("Create track failed", e)
|
||||
}
|
||||
}
|
||||
|
||||
public static loaded() {
|
||||
return this.track(Event.Create)
|
||||
}
|
||||
|
||||
public static dataReload() {
|
||||
return this.track(Event.Reload)
|
||||
}
|
||||
|
||||
public static settingsChanged(type: SettingsChangedType) {
|
||||
return this.track(Event.Settings, { type })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import powerbi from "powerbi-visuals-api"
|
||||
|
||||
export function VisualUpdateTypeToString(type: powerbi.VisualUpdateType) {
|
||||
switch (type) {
|
||||
case powerbi.VisualUpdateType.Resize:
|
||||
return "Resize"
|
||||
case powerbi.VisualUpdateType.ResizeEnd:
|
||||
return "ResizeEnd"
|
||||
case powerbi.VisualUpdateType.Style:
|
||||
return "Style"
|
||||
case powerbi.VisualUpdateType.ViewMode:
|
||||
return "ViewMode"
|
||||
case powerbi.VisualUpdateType.Resize + powerbi.VisualUpdateType.ResizeEnd:
|
||||
return "Resize+ResizeEnd"
|
||||
case powerbi.VisualUpdateType.Data:
|
||||
return "Data"
|
||||
case powerbi.VisualUpdateType.All:
|
||||
return "All"
|
||||
}
|
||||
}
|
||||
|
||||
export function cleanupDataColumnName(name: string) {
|
||||
var cleanName = name
|
||||
var simplePrefixes = ["First", "Last"]
|
||||
var compoundPrefixes = [
|
||||
"Count",
|
||||
"Sum",
|
||||
"Average",
|
||||
"Minimum",
|
||||
"Maximum",
|
||||
"Count",
|
||||
"Standard deviation",
|
||||
"Variance",
|
||||
"Median"
|
||||
].map(prefix => prefix + " of")
|
||||
|
||||
var prefixes = [...simplePrefixes, ...compoundPrefixes].map(
|
||||
prefix => prefix + " "
|
||||
)
|
||||
|
||||
for (let i = 0; i < prefixes.length; i++) {
|
||||
const prefix = prefixes[i]
|
||||
if (name.startsWith(prefix)) {
|
||||
cleanName = name.slice(prefix.length)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanName.startsWith("data.")) cleanName = cleanName.split("data.")[0]
|
||||
return cleanName
|
||||
}
|
||||
+129
-151
@@ -14,7 +14,9 @@ import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnume
|
||||
|
||||
import { SpeckleVisualSettings } from "./settings"
|
||||
import { Viewer, DefaultViewerParams } from "@speckle/viewer"
|
||||
|
||||
import * as _ from "lodash"
|
||||
import { VisualUpdateTypeToString, cleanupDataColumnName } from "./utils"
|
||||
import { SettingsChangedType, Tracker } from "./mixpanel"
|
||||
export class Visual implements IVisual {
|
||||
private target: HTMLElement
|
||||
private settings: SpeckleVisualSettings
|
||||
@@ -23,8 +25,33 @@ export class Visual implements IVisual {
|
||||
private selectionIdMap: Map<string, any>
|
||||
private viewer: Viewer
|
||||
|
||||
private updateTask: Promise<void>
|
||||
private ac = new AbortController()
|
||||
private currentOrthoMode: boolean = false
|
||||
private currentDefaultView: string = "default"
|
||||
|
||||
private debounceWait = 500
|
||||
|
||||
private debounceUpdate = _.debounce(options => {
|
||||
this.initViewer().then(async _ => {
|
||||
if (this.updateTask) {
|
||||
this.ac.abort()
|
||||
console.log("Cancelling previous load job")
|
||||
await this.updateTask
|
||||
this.ac = new AbortController()
|
||||
}
|
||||
// Handle changes in the visual objects
|
||||
this.handleSettingsUpdate(options)
|
||||
console.log("Updating viewer with new data")
|
||||
// Handle the update in data passed to this visual
|
||||
this.updateTask = this.handleDataUpdate(options, this.ac.signal).then(
|
||||
() => (this.updateTask = undefined)
|
||||
)
|
||||
})
|
||||
}, this.debounceWait)
|
||||
|
||||
constructor(options: VisualConstructorOptions) {
|
||||
console.log("Speckle 3D Visual constructor called", options)
|
||||
Tracker.loaded()
|
||||
this.host = options.host
|
||||
|
||||
this.selectionIdMap = new Map<string, any>()
|
||||
@@ -36,7 +63,6 @@ export class Visual implements IVisual {
|
||||
|
||||
public async initViewer() {
|
||||
if (this.viewer) {
|
||||
console.log("Viewer was already initialized. Skipping init call...")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -47,51 +73,24 @@ export class Visual implements IVisual {
|
||||
container.style.position = "fixed"
|
||||
|
||||
const params = DefaultViewerParams
|
||||
// Uncomment the line below to show stats
|
||||
params.showStats = true
|
||||
|
||||
const viewer = new Viewer(container, params)
|
||||
await viewer.init()
|
||||
|
||||
return viewer.init().then(() => {
|
||||
viewer.onWindowResize()
|
||||
// Setup any events here (progress, load-complete...)
|
||||
|
||||
viewer.on(
|
||||
"load-progress",
|
||||
(a: { progress: number; id: string; url: string }) => {
|
||||
this.loadedUrls[a.url] = a.progress
|
||||
if (a.progress >= 1) {
|
||||
viewer.onWindowResize()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
viewer.on("load-complete", () => {
|
||||
//console.log("Load complete")
|
||||
})
|
||||
|
||||
viewer.on("select", o => {
|
||||
if (o.location == null) return
|
||||
console.log("viewer object selected", o)
|
||||
//var ids = o.userData.map(data => this.selectionIdMap[data.id])
|
||||
// this.selectionManager.showContextMenu(ids[0] ?? {}, {
|
||||
// x: rect.top + o.location.x,
|
||||
// y: rect.left + o.location.y
|
||||
// })
|
||||
})
|
||||
|
||||
this.viewer = viewer
|
||||
})
|
||||
this.viewer = viewer
|
||||
}
|
||||
|
||||
private loadedUrls = {}
|
||||
|
||||
public update(options: VisualUpdateOptions) {
|
||||
this.settings = Visual.parseSettings(
|
||||
options && options.dataViews && options.dataViews[0]
|
||||
)
|
||||
|
||||
console.log(
|
||||
`Update was called with update type ${options.type.toString()}`,
|
||||
`Update was called with update type ${VisualUpdateTypeToString(
|
||||
options.type
|
||||
)}`,
|
||||
options,
|
||||
this.settings
|
||||
)
|
||||
@@ -109,43 +108,50 @@ export class Visual implements IVisual {
|
||||
}
|
||||
|
||||
console.log("Data was updated, updating viewer...")
|
||||
this.initViewer().then(_ => {
|
||||
// Handle changes in the visual objects
|
||||
this.handleSettingsUpdate(options)
|
||||
// Handle the update in data passed to this visual
|
||||
return this.handleDataUpdate(options)
|
||||
})
|
||||
this.debounceUpdate(options)
|
||||
}
|
||||
private currentOrthoMode: boolean = undefined
|
||||
private currentDefaultView: string = undefined
|
||||
private handleSettingsUpdate(options: VisualUpdateOptions) {
|
||||
|
||||
private async handleSettingsUpdate(options: VisualUpdateOptions) {
|
||||
// Handle change in ortho mode
|
||||
if (this.currentOrthoMode != this.settings.camera.orthoMode) {
|
||||
if (this.settings.camera.orthoMode)
|
||||
this.viewer?.cameraHandler?.setOrthoCameraOn()
|
||||
else this.viewer?.cameraHandler?.setPerspectiveCameraOn()
|
||||
this.currentOrthoMode = this.settings.camera.orthoMode
|
||||
Tracker.settingsChanged(SettingsChangedType.OrthoMode)
|
||||
}
|
||||
|
||||
// Handle change in default view
|
||||
if (this.currentDefaultView != this.settings.camera.defaultView) {
|
||||
this.viewer.interactions.rotateTo(this.settings.camera.defaultView)
|
||||
this.currentDefaultView = this.settings.camera.defaultView
|
||||
Tracker.settingsChanged(SettingsChangedType.DefaultCamera)
|
||||
}
|
||||
|
||||
// Update bg of viewer
|
||||
this.target.style.backgroundColor = this.settings.color.background
|
||||
}
|
||||
|
||||
private handleDataUpdate(options: VisualUpdateOptions) {
|
||||
private async handleDataUpdate(
|
||||
options: VisualUpdateOptions,
|
||||
signal: AbortSignal
|
||||
) {
|
||||
var categoricalView = options.dataViews[0].categorical
|
||||
var streamCategory = categoricalView?.categories[0].values
|
||||
var objectIdCategory = categoricalView?.categories[1].values
|
||||
var streamCategory = categoricalView?.categories[0]?.values
|
||||
var objectIdCategory = categoricalView?.categories[1]?.values
|
||||
var highlightedValues = categoricalView?.values
|
||||
? categoricalView?.values[0].highlights
|
||||
: null
|
||||
if (!streamCategory || !objectIdCategory) {
|
||||
// If some of the fields are not filled in, unload everything
|
||||
console.warn(
|
||||
`Incomplete data input. "Stream URL" and "Object ID" data inputs are mandatory`
|
||||
)
|
||||
await this.viewer.unloadAll()
|
||||
this.selectionIdMap = new Map<string, any>()
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Viewer loading:", options)
|
||||
//@ts-ignore
|
||||
var selectionBuilder = this.host.createSelectionIdBuilder()
|
||||
|
||||
@@ -153,132 +159,104 @@ export class Visual implements IVisual {
|
||||
var url = `${stream}/objects/${objectIdCategory[index]}`
|
||||
return url
|
||||
})
|
||||
|
||||
var objectsToUnload = []
|
||||
for (const key in this.selectionIdMap.keys()) {
|
||||
if (!objectUrls.find(url => url.split("/").slice(-1).pop() == key)) {
|
||||
for (const key of this.selectionIdMap.keys()) {
|
||||
const found = objectUrls.find(url => url == key)
|
||||
if (!found) {
|
||||
objectsToUnload.push(key)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Viewer loading ${objectUrls.length} and unloading ${objectsToUnload.length}`
|
||||
)
|
||||
var unloadPromises = objectsToUnload.map(url => {
|
||||
return this.viewer
|
||||
.unloadObject(url)
|
||||
|
||||
for (const url of objectsToUnload) {
|
||||
if (signal?.aborted) return
|
||||
await this.viewer
|
||||
.cancelLoad(url, true)
|
||||
.then(_ => {
|
||||
this.selectionIdMap.delete(url.split("/").slice(-1).pop())
|
||||
this.selectionIdMap.delete(url)
|
||||
})
|
||||
.catch(e => console.warn("Viewer Unload error", url, e))
|
||||
})
|
||||
}
|
||||
|
||||
var loadPromises = objectUrls.map((url, index) => {
|
||||
if (!this.selectionIdMap.has(url.split("/").slice(-1).pop())) {
|
||||
var index = 0
|
||||
for (const url of objectUrls) {
|
||||
if (signal?.aborted) return
|
||||
if (!this.selectionIdMap.has(url)) {
|
||||
var selectionId = selectionBuilder.withCategory(
|
||||
categoricalView?.categories[1].values[index]
|
||||
)
|
||||
return this.viewer
|
||||
await this.viewer
|
||||
.loadObject(url, null, false)
|
||||
.then(_ => {
|
||||
this.selectionIdMap.set(
|
||||
categoricalView?.categories[1].values[index].toString(),
|
||||
selectionId
|
||||
)
|
||||
var url =
|
||||
categoricalView?.categories[0].values[index].toString() +
|
||||
"/objects/" +
|
||||
categoricalView?.categories[1].values[index].toString()
|
||||
this.selectionIdMap.set(url, selectionId)
|
||||
})
|
||||
.catch(e => {
|
||||
console.warn("Viewer Load error", url, e)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
var unloadRes = Promise.all(unloadPromises)
|
||||
var loadRes = Promise.all(loadPromises)
|
||||
|
||||
return unloadRes
|
||||
.then(_ => loadRes)
|
||||
.then(_ => {
|
||||
var colorList = this.settings.color.getColorList()
|
||||
// Once everything is loaded, run the filter
|
||||
var filter = null
|
||||
if (categoricalView?.values) {
|
||||
var name = categoricalView?.values[0].source.displayName
|
||||
var isNum =
|
||||
categoricalView?.values[0].source.type.numeric ||
|
||||
categoricalView?.values[0].source.type.integer
|
||||
var filterType = isNum ? "gradient" : "category"
|
||||
console.log("filter:", filterType, name)
|
||||
if (highlightedValues)
|
||||
filter = {
|
||||
filterBy: {
|
||||
id: highlightedValues
|
||||
.map((value, index) =>
|
||||
value ? objectIdCategory[index] : null
|
||||
)
|
||||
.filter(e => e != null)
|
||||
},
|
||||
ghostOthers: true,
|
||||
colorBy: {
|
||||
type: filterType,
|
||||
property: this.cleanupDataColumnName(name),
|
||||
gradientColors: isNum ? colorList : undefined,
|
||||
minValue: categoricalView?.values[0].minLocal,
|
||||
maxValue: categoricalView?.values[0].maxLocal
|
||||
}
|
||||
}
|
||||
else
|
||||
filter = {
|
||||
filterBy: {
|
||||
id: objectIdCategory
|
||||
},
|
||||
colorBy: {
|
||||
type: filterType,
|
||||
property: this.cleanupDataColumnName(name),
|
||||
gradientColors: isNum ? colorList : undefined,
|
||||
minValue: categoricalView?.values[0].minLocal,
|
||||
maxValue: categoricalView?.values[0].maxLocal
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("filter:", filter)
|
||||
return this.viewer
|
||||
.applyFilter(filter)
|
||||
.catch(e => {
|
||||
console.warn("Filter failed to be applied. Filter will be reset", e)
|
||||
return this.viewer.applyFilter(null)
|
||||
})
|
||||
.then(_ => this.viewer.zoomExtents())
|
||||
})
|
||||
}
|
||||
private cleanupDataColumnName(name: string) {
|
||||
var cleanName = name
|
||||
var simplePrefixes = ["First", "Last"]
|
||||
var compoundPrefixes = [
|
||||
"Count",
|
||||
"Sum",
|
||||
"Average",
|
||||
"Minimum",
|
||||
"Maximum",
|
||||
"Count",
|
||||
"Standard deviation",
|
||||
"Variance",
|
||||
"Median"
|
||||
].map(prefix => prefix + " of")
|
||||
|
||||
var prefixes = [...simplePrefixes, ...compoundPrefixes].map(
|
||||
prefix => prefix + " "
|
||||
)
|
||||
|
||||
for (let i = 0; i < prefixes.length; i++) {
|
||||
const prefix = prefixes[i]
|
||||
if (name.startsWith(prefix)) {
|
||||
cleanName = name.slice(prefix.length)
|
||||
break
|
||||
}
|
||||
index++
|
||||
}
|
||||
|
||||
if (cleanName.startsWith("data.")) cleanName = cleanName.split("data.")[0]
|
||||
console.log("clean name", cleanName)
|
||||
return cleanName
|
||||
var colorList = this.settings.color.getColorList()
|
||||
// Once everything is loaded, run the filter
|
||||
var filter = null
|
||||
if (categoricalView?.values) {
|
||||
var name = categoricalView?.values[0].source.displayName
|
||||
var isNum =
|
||||
categoricalView?.values[0].source.type.numeric ||
|
||||
categoricalView?.values[0].source.type.integer
|
||||
var filterType = isNum ? "gradient" : "category"
|
||||
if (highlightedValues)
|
||||
filter = {
|
||||
filterBy: {
|
||||
id: highlightedValues
|
||||
.map((value, index) => (value ? objectIdCategory[index] : null))
|
||||
.filter(e => e != null)
|
||||
},
|
||||
ghostOthers: true,
|
||||
colorBy: {
|
||||
type: filterType,
|
||||
property: cleanupDataColumnName(name),
|
||||
gradientColors: isNum ? colorList : undefined,
|
||||
minValue: categoricalView?.values[0].minLocal,
|
||||
maxValue: categoricalView?.values[0].maxLocal
|
||||
}
|
||||
}
|
||||
else
|
||||
filter = {
|
||||
filterBy: {
|
||||
id: objectIdCategory
|
||||
},
|
||||
colorBy: {
|
||||
type: filterType,
|
||||
property: cleanupDataColumnName(name),
|
||||
gradientColors: isNum ? colorList : undefined,
|
||||
minValue: categoricalView?.values[0].minLocal,
|
||||
maxValue: categoricalView?.values[0].maxLocal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (signal?.aborted) return
|
||||
Tracker.dataReload()
|
||||
console.log("Applying filter:", filter)
|
||||
return await this.viewer
|
||||
.applyFilter(filter)
|
||||
.catch(e => {
|
||||
console.warn("Filter failed to be applied. Filter will be reset", e)
|
||||
return this.viewer.applyFilter(null)
|
||||
})
|
||||
.then(_ => this.viewer.zoomExtents())
|
||||
}
|
||||
|
||||
private static parseSettings(dataView: DataView): SpeckleVisualSettings {
|
||||
return <SpeckleVisualSettings>SpeckleVisualSettings.parse(dataView)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user