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