Commit 3e7044fb authored by Luna Riegel's avatar Luna Riegel
Browse files

Merge branch 'dev_object_catalogue' into 'dev'

Object Library

See merge request !1
parents 24f01efd 7fb28ca0
import { ToolboxType, WindowSlot, NotificationType } from '@vcmap/ui';
import { Cesium3DTileStyle, Math as CMath, Ellipsoid, Cartesian3, ConstantProperty, Transforms, HeadingPitchRoll, Cesium3DTileset, Matrix4 } from '@vcmap-cesium/engine';
import { AbstractInteraction, EventType } from '@vcmap/core';
import { name, version, mapVersion } from '../package.json';
import BIMOptions, { bimOptionsId } from './upload.vue';
import objectList, { objectListId } from './objectList.vue';
import scaleControl, { scaleControlId } from './scaleControl.vue';
import * as Cesium from "@vcmap-cesium/engine";
import {NotificationType, ToolboxType, WindowSlot} from '@vcmap/ui';
import * as Cesium from '@vcmap-cesium/engine';
import {
Cartesian3,
Cesium3DTileset,
Cesium3DTileStyle,
ConstantProperty,
Ellipsoid,
HeadingPitchRoll,
Math as CMath,
Transforms
} from '@vcmap-cesium/engine';
import {AbstractInteraction, EventType} from '@vcmap/core';
import {mapVersion, name, version} from '../package.json';
import BIMOptions, {bimOptionsId} from './upload.vue';
import objectList, {loadCatalogModel, objectListId} from './objectList.vue';
import scaleControl, {scaleControlId} from './scaleControl.vue';
let model;
......@@ -56,6 +65,7 @@ function setScale(scaleValue) {
const position = currentlyScaledModel.position.getValue();
const orientation = currentlyScaledModel.orientation;
const uri = currentlyScaledModel.model.uri;
const fromCatalog = currentlyScaledModel.fromCatalog;
vcMap.getEntities().remove(currentlyScaledModel);
removePlacedModel(currentlyScaledModel);
......@@ -70,6 +80,7 @@ function setScale(scaleValue) {
scale: scaleValue,
show: true,
},
fromCatalog: fromCatalog,
});
placedModels.push({
model: newModel,
......@@ -102,6 +113,7 @@ function saveObjects() {
position: [wgs84X, wgs84Y, height],
heading: heading,
scale: m.model.model.scale.getValue(),
fromCatalog: m.model.fromCatalog.valueOf(),
});
}
outputObject.placedObjects = outputArray;
......@@ -127,7 +139,7 @@ function saveFile(fileName, urlFile){
}
function setupModel(vcsApp, windowPos, url, name) {
function setupModel(vcsApp, windowPos, url, name, fromCatalog) {
const vcMap = vcsApp.maps.activeMap;
const scene = vcMap.getScene();
const ray = scene.camera.getPickRay(windowPos);
......@@ -141,6 +153,7 @@ function setupModel(vcsApp, windowPos, url, name) {
show: true,
scale: 1,
},
fromCatalog: fromCatalog,
});
placedModels.push({
model: model,
......@@ -180,7 +193,6 @@ class MoveInteraction extends AbstractInteraction {
let xDif = pickResult.x - initialPickResult.x;
let yDif = pickResult.y - initialPickResult.y;
let zDif = pickResult.z - initialPickResult.z;
// pickResult.z = pickResult.z + heightDif;
let pos = initialPosition.clone();
pos.x = pos.x + xDif;
pos.y = pos.y + yDif;
......@@ -289,8 +301,7 @@ class RotateAction extends AbstractInteraction {
if (hpRoll.heading < 0.0) {
hpRoll.heading += CMath.TWO_PI;
}
const orientation = new ConstantProperty(Transforms.headingPitchRollQuaternion(modelPos, hpRoll));
model.orientation = orientation;
model.orientation = new ConstantProperty(Transforms.headingPitchRollQuaternion(modelPos, hpRoll));
}
return event;
}
......@@ -343,7 +354,7 @@ export default function smartVillagesPlugin(config, baseUrl) {
const objects = JSON.parse(content);
const vcMap = app.maps.activeMap;
for (const loadingObject of objects.placedObjects) {
let objectUrl = undefined;
let objectUrl;
for (const loadedObject of pluginState.objects) {
if (loadedObject.name === loadingObject.name) {
objectUrl = loadedObject.url;
......@@ -351,12 +362,29 @@ export default function smartVillagesPlugin(config, baseUrl) {
}
}
if (objectUrl === undefined) {
app.notifier.add({
type: NotificationType.ERROR,
message: "Objekt mit dem namen: " + loadingObject.name + " konnte nicht in den geladenen Objekten gefunden werden. Konfiguration konnte nicht vollständig geladen werden.",
});
// abort
return;
if (loadingObject.fromCatalog === true){
// Load the model into memory
const model = await loadCatalogModel(loadingObject.name).catch(e => {
console.error(e);
app.notifier.add({
type: NotificationType.ERROR,
message: "Katalogobject: " + loadingObject.name + " konnte nicht geladenen werden. Konfiguration konnte nicht vollständig geladen werden.",
});
return undefined;
});
if (model === undefined) {
return;
} else {
objectUrl = model.url;
}
}else {
app.notifier.add({
type: NotificationType.ERROR,
message: "Objekt mit dem namen: " + loadingObject.name + " konnte nicht in den geladenen Objekten gefunden werden. Konfiguration konnte nicht vollständig geladen werden.",
});
// abort
return;
}
}
const cartPos = Cartesian3.fromDegrees(loadingObject.position[0], loadingObject.position[1], loadingObject.position[2]);
// Add the model to the viewer
......@@ -367,13 +395,13 @@ export default function smartVillagesPlugin(config, baseUrl) {
uri: objectUrl,
scale: loadingObject.scale,
show: true,
fromCatalog: loadingObject.fromCatalog,
},
});
const roll = new HeadingPitchRoll();
hpRollMap.set(model, roll);
roll.heading = loadingObject.heading;
const orientation = new ConstantProperty(Transforms.headingPitchRollQuaternion(model.position.getValue(), roll));
model.orientation = orientation;
model.orientation = new ConstantProperty(Transforms.headingPitchRollQuaternion(model.position.getValue(), roll));
placedModels.push({
model: model,
......@@ -527,11 +555,6 @@ export default function smartVillagesPlugin(config, baseUrl) {
}
const map = vcsApp.maps.getByType("CesiumVisualisationType");
console.log(map);
// tileset.allTilesLoaded.addEventListener(function() {
// console.log('All tiles are loaded');
// });
const bimGroup = {
id: 'bim-functions',
......
......@@ -6,10 +6,9 @@
<span>Die Objektbibliothek.</span>
</template>
<template #default>
<v-expansion-panels v-for = "(subcats, categoryName) in objLib" :key="categoryName">
<v-expansion-panel >
<v-expansion-panel-title class="text-h5">{{ categoryName }}</v-expansion-panel-title>
<v-container v-for="(entries, subCategory) in subcats" :key = "subCategory" class=" py-1 px-1 border-b-md" expandable>
<v-expansion-panels multiple>
<VcsExpansionPanel v-for = "(subcats, categoryName) in objLib" :key="categoryName" :heading="categoryName">
<v-container v-for="(entries, subCategory) in subcats" :key = "subCategory" class=" py-1 px-1 border-b-md">
<v-col>
<VcsLabel class="mb-4 text-h2">{{subCategory}}</VcsLabel>
<v-list>
......@@ -17,7 +16,7 @@
<vcs-button
v-for="entry in entries"
:key="entry.title"
@click="fetchCatalogueModel(entry.model)"
@click="placeCatalogModel(entry.model)"
class="p-0 mx-1 my-0 border gc-2"
style="width: auto; height: auto; margin-left: 1rem; margin-bottom: 1px;"
variant="filled"
......@@ -29,7 +28,7 @@
</v-col>
<v-divider/>
</v-container>
</v-expansion-panel>
</VcsExpansionPanel>
</v-expansion-panels>
</template>
......@@ -113,7 +112,7 @@ import {
VcsTreeview,
VcsTreeviewTitle,
} from '@vcmap/ui';
import {VCard, VCol, VContainer, VDialog, VForm, VRow, VSheet, VSwitch,} from 'vuetify/components';
import {VCard, VCol, VContainer, VDialog, VForm, VRow, VSheet, VSwitch, VExpansionPanels} from 'vuetify/components';
import {computed, inject, onMounted, ref} from 'vue';
import {name} from '../package.json';
import * as THREE from 'three';
......@@ -126,21 +125,21 @@ const preloadedModels = ref({});
const defaultItems = [];
const items = ref(defaultItems);
let setupModelFunction = undefined;
let app = undefined;
let projectStateObject = undefined;
let setupModelFunction;
let app;
let projectStateObject;
async function loadCataloguesFromConfig(config) {
async function loadCatalogsFromConfig(config) {
const configJson = JSON.parse(JSON.stringify(config));
const catalogues = Object.values(configJson.objectCatalogues);
for (const cat of catalogues) {
const catJson = await retrieveCatalogueJson(cat.url);
const catJson = await retrieveCatalogJson(cat.url);
if (!catJson) continue;
const category = cat.category.toString();
const linkPrefix = cat.url.replace(/\/[^/]*catalog\.json$/, '/');
const entries = getCatalogueEntries(catJson, linkPrefix);
const entries = getCatalogEntries(catJson, linkPrefix);
if (!objLib.value[category]) {
objLib.value[category] = entries;
......@@ -152,21 +151,22 @@ async function loadCataloguesFromConfig(config) {
}
async function retrieveCatalogueJson(link) {
async function retrieveCatalogJson(link) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch(link, { signal: controller.signal });
if (!response.ok) {
throw new Error(`HTTP error ${response.status} for ${link}`);
console.error(`Received HTTP error ${response.status} for ${link}`);
return null;
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.warn(`Fetch aborted (timeout) for: ${link}`);
} else {
console.error(`Fetch failed for ${link}:`, error);
console.error(`Catalog.json request timed out: ${link}`);
}else{
console.error(`Encountered error during catalog.json request: ${link}`, error);
}
return null;
} finally {
......@@ -174,7 +174,7 @@ async function retrieveCatalogueJson(link) {
}
}
function getCatalogueEntries(json, linkPrefix) {
function getCatalogEntries(json, linkPrefix) {
const part = {};
......@@ -210,72 +210,75 @@ function getCatalogueEntries(json, linkPrefix) {
return part;
}
async function retrieveCatalogModel(modelLink){
if(preloadedModels.value[modelLink] != null){
const catModel = preloadedModels.value[modelLink];
async function placeCatalogModel(modelLink){
loadCatalogModel(modelLink).then(model => {
const catModel = model;
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const cart = new Cartesian2(x, y);
setupModelFunction(app, cart, catModel.url, catModel.modelLink);
} else {
const loader = new GLTFLoader();
loader.load(
modelLink,
function (gltf) {
const bounds = new THREE.Box3().setFromObject(gltf.scene);
const x = bounds.min.x + (bounds.max.x - bounds.min.x) / 2;
const z = bounds.min.z + (bounds.max.z - bounds.min.z) / 2;
const m = new THREE.Matrix4().makeTranslation(-x, -bounds.min.y, -z);
gltf.scene.applyMatrix4(m);
const exporter = new GLTFExporter();
const options = {
binary: true,
animations: gltf.animations,
};
setupModelFunction(app, cart, catModel.url, catModel.modelLink, true);
}).catch(error => {
console.error(error);
app.notifier.add({
type: NotificationType.ERROR,
message:
'Ein Fehler ist beim Laden des Katalogobjektes aufgetreten.',
timeout: 20000,
});
});
}
exporter.parse(
gltf.scene,
function (output) {
const blob = new Blob([output], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
projectStateObject.objects.push({
name: modelLink,
url: url,
});
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const cart = new Cartesian2(x, y);
setupModelFunction(app, cart, url, modelLink);
preloadedModels.value[modelLink] = {
url: url,
modelLink: modelLink,
};
},
function (error) {
app.notifier.add({
type: NotificationType.ERROR,
message:
'Ein Fehler ist beim Laden der glTF Datei aufgetreten. Bitte vergewissern Sie sich, dass es sich um eine gültige glTF2.0 Datei handelt.',
timeout: 20000,
});
},
options
);
},
undefined,
function (error) {
app.notifier.add({
type: NotificationType.ERROR,
message:
'Ein Fehler ist beim Laden der glTF Datei aufgetreten. Bitte vergewissern Sie sich, dass es sich um eine gültige glTF2.0 Datei handelt.' +
' Fehlermeldung: ' + error.message,
timeout: 20000,
});
}
);
}
export async function loadCatalogModel(modelLink){
return new Promise( (resolve, reject) => {
if(preloadedModels.value[modelLink] != null){
// Model is already in memory
resolve(preloadedModels.value[modelLink]);
} else{
const loader = new GLTFLoader();
loader.load(
modelLink,
function (gltf) {
const bounds = new THREE.Box3().setFromObject(gltf.scene);
const x = bounds.min.x + (bounds.max.x - bounds.min.x) / 2;
const z = bounds.min.z + (bounds.max.z - bounds.min.z) / 2;
const m = new THREE.Matrix4().makeTranslation(-x, -bounds.min.y, -z);
gltf.scene.applyMatrix4(m);
const exporter = new GLTFExporter();
const options = {
binary: true,
animations: gltf.animations,
};
exporter.parse(
gltf.scene,
function (output) {
const blob = new Blob([output], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
projectStateObject.objects.push({
name: modelLink,
url: url,
});
preloadedModels.value[modelLink] = {
url: url,
modelLink: modelLink,
};
resolve(preloadedModels.value[modelLink]);
},
function (error) {
reject(error);
},
options
);
},
undefined,
function (error) {
reject(error);
}
);
}
})
}
function uploadModel() {
......@@ -303,8 +306,6 @@ function uploadModel() {
});
let url = URL.createObjectURL(blob);
const loader = new GLTFLoader();
loader.load(url, function(gltf) {
let bounds = new THREE.Box3().setFromObject(gltf.scene);
......@@ -337,7 +338,7 @@ function uploadModel() {
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const cart = new Cartesian2(x, y);
setupModelFunction(app, cart, url, name);
setupModelFunction(app, cart, url, name, false);
},
}
]
......@@ -346,10 +347,11 @@ function uploadModel() {
},
// called when there is an error in the generation
function ( error ) {
function ( error ) {
app.notifier.add({
type: NotificationType.ERROR,
message: "Ein Fehler ist beim Laden der glTF Datei aufgetreten. Bitte vergewissern Sie sich, dass es sich um eine gültige glTF2.0 Datei handelt.",
message: "Ein Fehler ist beim Laden der glTF Datei aufgetreten. Bitte vergewissern Sie sich, dass es sich um eine gültige glTF2.0 Datei handelt." +
" Fehlermeldung: " + error.message,
timeout: 20000,
});
}, options);
......@@ -362,8 +364,6 @@ function uploadModel() {
});
});
}
reader.readAsArrayBuffer(file);
}
});
......@@ -403,6 +403,7 @@ function uploadModel() {
VContainer,
VRow,
VCol,
VExpansionPanels,
},
setup() {
app = inject('vcsApp');
......@@ -430,8 +431,7 @@ function uploadModel() {
const dialog = ref(false);
setupModelFunction = setupModel;
projectStateObject = pluginState;
// loadCataloguesFromConfig(config).then(response => {console.log(objLib);});
onMounted(()=>loadCataloguesFromConfig(config).then(response => {console.log(objLib)}));
onMounted(()=>loadCatalogsFromConfig(config).then(() => {console.log("Object library loaded")}));
return {
draggable,
selectable,
......@@ -525,8 +525,8 @@ function uploadModel() {
loadModel() {
uploadModel();
},
fetchCatalogueModel(modelLink) {
retrieveCatalogModel(modelLink);
placeCatalogModel(modelLink) {
placeCatalogModel(modelLink);
},
loadConfiguration() {
loadObjects(app);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment