Commit 12fb48eb authored by Matthias Betz's avatar Matthias Betz
Browse files

platzieren mit geolocation

parent 3e7044fb
import {NotificationType, ToolboxType, WindowSlot} from '@vcmap/ui';
import { NotificationType, ToolboxType, WindowSlot } from '@vcmap/ui';
import * as Cesium from '@vcmap-cesium/engine';
import {
Cartesian3,
......@@ -8,14 +8,13 @@ import {
Ellipsoid,
HeadingPitchRoll,
Math as CMath,
Transforms
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';
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 conditions = [[true, true]];
......@@ -30,15 +29,18 @@ let hpRoll;
let app;
let currentlyScaledModel;
const hidingFeatures = [];
const hidingListener = function(tile) {
const hidingListener = function (tile) {
let allHidden = true;
for (const featureToHide of hidingFeatures) {
if (featureToHide.loops < 10) {
allHidden = false;
const feature = tile.content.getFeature(featureToHide.id);
if (feature !== undefined) {
app.maps.activeMap.layerCollection.globalHider.addFeature("manual", feature);
app.maps.activeMap.layerCollection.globalHider.hideObjects(["manual"]);
app.maps.activeMap.layerCollection.globalHider.addFeature(
'manual',
feature,
);
app.maps.activeMap.layerCollection.globalHider.hideObjects(['manual']);
featureToHide.loops++;
}
}
......@@ -55,6 +57,12 @@ const hidingListener = function(tile) {
}
};
const pluginState = {
files: [],
objects: [],
geolocations: {},
};
function updateHideCondition(gmlId) {
conditions.unshift(['${id} === "' + gmlId + '"', false]);
}
......@@ -104,7 +112,9 @@ function saveObjects() {
if (tempRoll !== undefined) {
heading = tempRoll.heading;
}
const ellipPos = Ellipsoid.WGS84.cartesianToCartographic(m.model.position.getValue());
const ellipPos = Ellipsoid.WGS84.cartesianToCartographic(
m.model.position.getValue(),
);
const wgs84X = CMath.toDegrees(ellipPos.longitude);
const wgs84Y = CMath.toDegrees(ellipPos.latitude);
const height = ellipPos.height;
......@@ -123,13 +133,13 @@ function saveObjects() {
}
outputObject.hiddenObjects = hiddenObjects;
let textData = JSON.stringify(outputObject);
let blobData = new Blob([textData], {type: "application/json"});
let blobData = new Blob([textData], { type: 'application/json' });
saveFile('ObjektPositionierung.json', window.URL.createObjectURL(blobData));
}
function saveFile(fileName, urlFile){
let a = document.createElement("a");
a.style = "display: none";
function saveFile(fileName, urlFile) {
let a = document.createElement('a');
a.style = 'display: none';
document.body.appendChild(a);
a.href = urlFile;
a.download = fileName;
......@@ -138,12 +148,79 @@ function saveFile(fileName, urlFile){
a.remove();
}
async function placeModelAtPosition(vcMap, lon, lat, modelUrl, name) {
// Create Cartographic position
const position = Cesium.Cartographic.fromDegrees(lon, lat);
// Wait for terrain height
const [updatedPosition] = await Cesium.sampleTerrainMostDetailed(
vcMap.getCesiumWidget().terrainProvider,
[position],
);
const height = updatedPosition.height + 10;
console.log("Height: " + height);
console.log("Lon: " + lon);
console.log("Lat: " + lat);
const cartesianPosition = Cartesian3.fromDegrees(lon, lat, height);
console.log(cartesianPosition);
// Add the model to the viewer
model = vcMap.getEntities().add({
name: name,
position: cartesianPosition,
model: {
uri: modelUrl,
show: true,
scale: 1,
},
fromCatalog: false,
});
placedModels.push({
model: model,
name: name,
});
vcMap.requestRender();
vcMap.getCesiumWidget().camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, 1500),
orientation: {
heading: Cesium.Math.toRadians(0.0), // rotation from north
pitch: Cesium.Math.toRadians(-45.0), // negative to look down
roll: 0.0,
},
duration: 2.0, // seconds
});
}
function setupModel(vcsApp, windowPos, url, name, fromCatalog) {
const vcMap = vcsApp.maps.activeMap;
const geolocation = pluginState.geolocations[name];
if (geolocation === undefined) {
setupModelWithoutGeolocation(
vcMap,
windowPos,
center,
name,
url,
fromCatalog,
vcsApp,
);
} else {
const lon = geolocation.lon;
const lat = geolocation.lat;
placeModelAtPosition(vcMap, lon, lat, url, name);
}
}
function setupModelWithoutGeolocation(
vcMap,
windowPos,
center,
name,
url,
fromCatalog,
vcsApp,
) {
const scene = vcMap.getScene();
const ray = scene.camera.getPickRay(windowPos);
const center = scene.globe.pick(ray, scene);
center = scene.globe.pick(ray, scene);
// Add the model to the viewer
model = vcMap.getEntities().add({
name: name,
......@@ -162,11 +239,14 @@ function setupModel(vcsApp, windowPos, url, name, fromCatalog) {
vcMap.requestRender();
const interaction = new PickPlaceInteraction();
interaction.setActive(EventType.CLICKMOVE);
activeMouseEvent = vcsApp.maps.eventHandler.addExclusiveInteraction(interaction, () => {});
activeMouseEvent = vcsApp.maps.eventHandler.addExclusiveInteraction(
interaction,
() => {},
);
}
function removePlacedModel(modelToBeRemoved) {
placedModels = placedModels.filter(function(m) {
placedModels = placedModels.filter(function (m) {
return m.model !== modelToBeRemoved;
});
}
......@@ -180,10 +260,7 @@ class MoveInteraction extends AbstractInteraction {
} else if (event.pointerEvent === 3 && model !== undefined) {
const cesiumMap = event.map;
const scene = cesiumMap.getScene();
const pickResult = scene.globe.pick(
event.ray,
scene
);
const pickResult = scene.globe.pick(event.ray, scene);
if (initialPickResult === undefined) {
initialPickResult = pickResult;
}
......@@ -204,7 +281,6 @@ class MoveInteraction extends AbstractInteraction {
}
}
class PickPlaceInteraction extends AbstractInteraction {
async pipe(event) {
if (event.pointerEvent === 2) {
......@@ -214,10 +290,7 @@ class PickPlaceInteraction extends AbstractInteraction {
} else if (event.pointerEvent === 3 && model !== undefined) {
const cesiumMap = event.map;
const scene = cesiumMap.getScene();
const pickResult = scene.globe.pick(
event.ray,
scene
);
const pickResult = scene.globe.pick(event.ray, scene);
pickResult.z = pickResult.z + heightDif;
model.position.setValue(pickResult);
}
......@@ -252,11 +325,15 @@ class HeightInteraction extends AbstractInteraction {
if (dif === 0) {
return event;
}
heightDif = heightDif - (dif / 50);
heightDif = heightDif - dif / 50;
let pos = model.position.getValue();
let cart = Cesium.Cartographic.fromCartesian(pos);
cart.height -= (dif/50);
pos = Cesium.Cartesian3.fromRadians(cart.longitude, cart.latitude, cart.height);
cart.height -= dif / 50;
pos = Cesium.Cartesian3.fromRadians(
cart.longitude,
cart.latitude,
cart.height,
);
currentMousePosition = [event.windowPosition.x, event.windowPosition.y];
model.position.setValue(pos);
}
......@@ -301,7 +378,9 @@ class RotateAction extends AbstractInteraction {
if (hpRoll.heading < 0.0) {
hpRoll.heading += CMath.TWO_PI;
}
model.orientation = new ConstantProperty(Transforms.headingPitchRollQuaternion(modelPos, hpRoll));
model.orientation = new ConstantProperty(
Transforms.headingPitchRollQuaternion(modelPos, hpRoll),
);
}
return event;
}
......@@ -313,11 +392,6 @@ class RotateAction extends AbstractInteraction {
* @returns {import("@vcmap/ui/src/vcsUiApp").VcsPlugin<PluginConfig, PluginState>}
*/
export default function smartVillagesPlugin(config, baseUrl) {
const pluginState = {
files: [],
objects: [],
};
return {
get name() {
return name;
......@@ -334,22 +408,22 @@ export default function smartVillagesPlugin(config, baseUrl) {
saveObjects,
loadObjects(app) {
// Create an input element
const inputElement = document.createElement("input");
const inputElement = document.createElement('input');
// Set its type to file
inputElement.type = "file";
inputElement.type = 'file';
// Set accept to the file types you want the user to select.
// Include both the file extension and the mime type
inputElement.accept = ".json, .txt";
inputElement.accept = '.json, .txt';
// set onchange event to call callback when user has selected file
inputElement.addEventListener("change", e => {
inputElement.addEventListener('change', (e) => {
const fileInput = e.target;
const file = fileInput.files[0];
const reader = new FileReader();
reader.onload = async readerEvent => {
reader.onload = async (readerEvent) => {
const content = readerEvent.target.result;
const objects = JSON.parse(content);
const vcMap = app.maps.activeMap;
......@@ -362,31 +436,44 @@ export default function smartVillagesPlugin(config, baseUrl) {
}
}
if (objectUrl === undefined) {
if (loadingObject.fromCatalog === true){
if (loadingObject.fromCatalog === true) {
// Load the model into memory
const model = await loadCatalogModel(loadingObject.name).catch(e => {
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.",
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 {
} 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.",
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],
);
console.log(cartPos);
// Add the model to the viewer
model = vcMap.getEntities().add({
name: loadingObject.name,
......@@ -401,7 +488,12 @@ export default function smartVillagesPlugin(config, baseUrl) {
const roll = new HeadingPitchRoll();
hpRollMap.set(model, roll);
roll.heading = loadingObject.heading;
model.orientation = new ConstantProperty(Transforms.headingPitchRollQuaternion(model.position.getValue(), roll));
model.orientation = new ConstantProperty(
Transforms.headingPitchRollQuaternion(
model.position.getValue(),
roll,
),
);
placedModels.push({
model: model,
......@@ -430,13 +522,12 @@ export default function smartVillagesPlugin(config, baseUrl) {
});
}
}
}
};
reader.readAsText(file);
});
// dispatch a click event to open the file dialog
inputElement.dispatchEvent(new MouseEvent("click"));
inputElement.dispatchEvent(new MouseEvent('click'));
},
get config() {
return config;
......@@ -446,9 +537,15 @@ export default function smartVillagesPlugin(config, baseUrl) {
vcsApp.contextMenuManager.addEventHandler(async (event) => {
const actions = [];
const pick = vcsApp.maps.activeMap.getScene().pick(event.windowPosition);
if (pick !== undefined && pick.primitive !== undefined && pick.primitive.id !== undefined) {
model = pick.primitive.id
const pick = vcsApp.maps.activeMap
.getScene()
.pick(event.windowPosition);
if (
pick !== undefined &&
pick.primitive !== undefined &&
pick.primitive.id !== undefined
) {
model = pick.primitive.id;
actions.push({
id: 'move',
name: 'Objekt verschieben',
......@@ -457,7 +554,13 @@ export default function smartVillagesPlugin(config, baseUrl) {
initialPickResult = undefined;
const interaction = new MoveInteraction();
interaction.setActive(EventType.CLICKMOVE);
activeMouseEvent = vcsApp.maps.eventHandler.addExclusiveInteraction(interaction, () => { console.log("removed") });
activeMouseEvent =
vcsApp.maps.eventHandler.addExclusiveInteraction(
interaction,
() => {
console.log('removed');
},
);
},
});
actions.push({
......@@ -466,7 +569,13 @@ export default function smartVillagesPlugin(config, baseUrl) {
callback() {
const interaction = new HeightInteraction();
interaction.setActive(EventType.CLICKMOVE);
activeMouseEvent = vcsApp.maps.eventHandler.addExclusiveInteraction(interaction, () => { console.log("removed") });
activeMouseEvent =
vcsApp.maps.eventHandler.addExclusiveInteraction(
interaction,
() => {
console.log('removed');
},
);
},
});
actions.push({
......@@ -480,7 +589,13 @@ export default function smartVillagesPlugin(config, baseUrl) {
}
const interaction = new RotateAction();
interaction.setActive(EventType.CLICKMOVE);
activeMouseEvent = vcsApp.maps.eventHandler.addExclusiveInteraction(interaction, () => { console.log("removed") });
activeMouseEvent =
vcsApp.maps.eventHandler.addExclusiveInteraction(
interaction,
() => {
console.log('removed');
},
);
},
});
......@@ -495,12 +610,12 @@ export default function smartVillagesPlugin(config, baseUrl) {
component: scaleControl,
slot: WindowSlot.DYNAMIC_LEFT,
state: {
headerTitle: "Skalierung",
headerTitle: 'Skalierung',
},
},
name,
);
}
},
});
actions.push({
......@@ -511,13 +626,12 @@ export default function smartVillagesPlugin(config, baseUrl) {
removePlacedModel(model);
},
});
} else if (event.feature) {
actions.push({
id: 'delete',
name: 'Objekt verstecken',
callback() {
const gmlId = event.feature.getProperty("id");
const gmlId = event.feature.getProperty('id');
if (gmlId !== undefined) {
updateHideCondition(gmlId);
event.feature.tileset.style = new Cesium3DTileStyle({
......@@ -531,12 +645,15 @@ export default function smartVillagesPlugin(config, baseUrl) {
});
} else {
const { activeMap } = vcsApp.maps;
activeMap.layerCollection.globalHider.addFeature("manual", event.feature);
activeMap.layerCollection.globalHider.addFeature(
'manual',
event.feature,
);
hidingFeatures.push({
id: event.feature.featureId,
loops: 0,
});
activeMap.layerCollection.globalHider.hideObjects(["manual"]);
activeMap.layerCollection.globalHider.hideObjects(['manual']);
}
},
});
......@@ -550,23 +667,14 @@ export default function smartVillagesPlugin(config, baseUrl) {
* @returns {Promise<void>}
*/
async onVcsAppMounted(vcsApp) {
for (const map of vcsApp.maps) {
console.log(map);
}
const map = vcsApp.maps.getByType("CesiumVisualisationType");
const bimGroup = {
id: 'bim-functions',
type: ToolboxType.GROUP,
icon: "ifc",
icon: 'ifc',
disabled: false,
title: 'BIM Funktionen',
};
vcsApp.toolboxManager.add(
bimGroup,
name,
);
vcsApp.toolboxManager.add(bimGroup, name);
/**
* @type {Array<import("@vcmap/ui").ButtonComponentOptions>}
......@@ -589,7 +697,7 @@ export default function smartVillagesPlugin(config, baseUrl) {
width: 400,
},
state: {
headerTitle: "IFC nach gltf Konvertierung",
headerTitle: 'IFC nach gltf Konvertierung',
},
},
name,
......@@ -611,7 +719,7 @@ export default function smartVillagesPlugin(config, baseUrl) {
component: objectList,
slot: WindowSlot.DYNAMIC_LEFT,
state: {
headerTitle: "Objektverwaltung",
headerTitle: 'Objektverwaltung',
},
},
name,
......@@ -631,15 +739,10 @@ export default function smartVillagesPlugin(config, baseUrl) {
},
},
},
];
const groupButtonManager = vcsApp.toolboxManager.get(
'bim-functions',
).buttonManager;
buttonComponents.forEach((b) =>
groupButtonManager.add(b, name),
);
const groupButtonManager =
vcsApp.toolboxManager.get('bim-functions').buttonManager;
buttonComponents.forEach((b) => groupButtonManager.add(b, name));
},
/**
* @param {boolean} forUrl
......@@ -655,8 +758,7 @@ export default function smartVillagesPlugin(config, baseUrl) {
const options = {};
return options;
},
i18n: {
},
i18n: {},
destroy() {
// empty
},
......
......@@ -7,26 +7,22 @@
</template>
<template #default>
<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>
<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 class="list-col">
<VcsLabel class="mb-4 text-h2">{{ subCategory }}</VcsLabel>
<v-list class="no-scroll">
<v-row>
<vcs-button
v-for="entry in entries"
:key="entry.title"
@click="placeCatalogModel(entry.model)"
<vcs-button v-for="entry in entries" :key="entry.title" @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"
>
<img :src="entry.icon" :alt="entry.icon" style="display: block; max-width: 100%; max-height: 100%;" />
style="width: auto; height: auto; margin-left: 1rem; margin-bottom: 1px;" variant="filled">
<img :src="entry.icon" :alt="entry.icon"
style="display: block; max-width: 100%; max-height: 100%;" />
</vcs-button>
</v-row>
</v-list>
</v-col>
<v-divider/>
<v-divider />
</v-container>
</VcsExpansionPanel>
</v-expansion-panels>
......@@ -46,20 +42,10 @@
<v-container class="py-1 px-1">
<v-row no-gutters>
<vcs-list
:items="items"
:draggable="draggable"
:selectable="selectable"
:single-select="selectSingle"
:searchable="searchable"
:show-title="showTitle"
:icon="titleIconSrc"
:actions="titleActionsArray"
:title="title"
v-model="selected"
@item-moved="move"
@item-renamed="({ item, newTitle }) => (item.title = newTitle)"
/>
<vcs-list :items="items" :draggable="draggable" :selectable="selectable" :single-select="selectSingle"
:searchable="searchable" :show-title="showTitle" :icon="titleIconSrc" :actions="titleActionsArray"
:title="title" v-model="selected" @item-moved="move"
@item-renamed="({ item, newTitle }) => (item.title = newTitle)" />
</v-row>
</v-container>
</VcsFormSection>
......@@ -80,7 +66,8 @@
<span>Hier können versteckte Gebäude und neu platzierte Objekte an ihrer jetzigen Position gespeichert werden
und wieder geladen werden.
</span>
<p><b>Wichtig:</b> Vor dem Laden einer Positionsdatei müssen die in der Datei beschriebenen Gebäude vorher in die Objektliste geladen werden.
<p><b>Wichtig:</b> Vor dem Laden einer Positionsdatei müssen die in der Datei beschriebenen Gebäude vorher in
die Objektliste geladen werden.
Dies geschieht nicht automatisch mit dem Laden der Positionsdatei.</p>
</template>
<v-container class="py-1 px-1">
......@@ -96,7 +83,7 @@
</v-row>
</v-container>
</VcsFormSection>
</v-sheet>
</v-sheet>
</template>
<script>
......@@ -112,13 +99,13 @@ import {
VcsTreeview,
VcsTreeviewTitle,
} from '@vcmap/ui';
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 { VCard, VCol, VContainer, VDialog, VForm, VRow, VSheet, VSwitch, VExpansionPanels, VList } from 'vuetify/components';
import { computed, inject, onMounted, ref } from 'vue';
import { name } from '../package.json';
import * as THREE from 'three';
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
import {GLTFExporter} from 'three/addons/exporters/GLTFExporter.js';
import {Cartesian2} from '@vcmap-cesium/engine';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
import { Cartesian2 } from '@vcmap-cesium/engine';
const objLib = ref({});
const preloadedModels = ref({});
......@@ -129,6 +116,33 @@ let setupModelFunction;
let app;
let projectStateObject;
function addModelToObjectList(name, url) {
if (projectStateObject === undefined) {
// object library wasn't opened before
return;
}
projectStateObject.objects.push({
name: name,
url: url,
});
const item = {
name: name,
title: name,
actions: [
{
name: 'Objekt platzieren',
callback() {
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const cart = new Cartesian2(x, y);
setupModelFunction(app, cart, url, name, false);
},
}
]
};
items.value.push(item);
}
async function loadCatalogsFromConfig(config) {
const configJson = JSON.parse(JSON.stringify(config));
const catalogues = Object.values(configJson.objectCatalogues);
......@@ -165,7 +179,7 @@ async function retrieveCatalogJson(link) {
} catch (error) {
if (error.name === 'AbortError') {
console.error(`Catalog.json request timed out: ${link}`);
}else{
} else {
console.error(`Encountered error during catalog.json request: ${link}`, error);
}
return null;
......@@ -208,9 +222,9 @@ function getCatalogEntries(json, linkPrefix) {
}
return part;
}
}
async function placeCatalogModel(modelLink){
async function placeCatalogModel(modelLink) {
loadCatalogModel(modelLink).then(model => {
const catModel = model;
const x = window.innerWidth / 2;
......@@ -228,12 +242,12 @@ async function placeCatalogModel(modelLink){
});
}
export async function loadCatalogModel(modelLink){
return new Promise( (resolve, reject) => {
if(preloadedModels.value[modelLink] != null){
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{
} else {
const loader = new GLTFLoader();
loader.load(
modelLink,
......@@ -281,33 +295,9 @@ export async function loadCatalogModel(modelLink){
})
}
function uploadModel() {
// Create an input element
const inputElement = document.createElement("input");
// Set its type to file
inputElement.type = "file";
inputElement.setAttribute("multiple", "");
// Set accept to the file types you want the user to select.
// Include both the file extension and the mime type
inputElement.accept = ".glb, .gltf";
// set onchange event to call callback when user has selected file
inputElement.addEventListener("change", e => {
const fileInput = e.target;
for (const file of fileInput.files) {
const reader = new FileReader();
reader.onload = async readerEvent => {
const content = readerEvent.target.result;
let blob = new Blob([content], {
type: "application/octet-stream",
});
let url = URL.createObjectURL(blob);
export function loadModel(url, name) {
const loader = new GLTFLoader();
loader.load(url, function(gltf) {
loader.load(url, function (gltf) {
let 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;
......@@ -321,33 +311,12 @@ function uploadModel() {
exporter.parse(gltf.scene,
// called when the gltf has been generated
function (output) {
blob = new Blob([output]);
const blob = new Blob([output]);
url = URL.createObjectURL(blob);
const name = file.name;
projectStateObject.objects.push({
name: name,
url: url,
});
const item = {
name: name,
title: name,
actions: [
{
name: 'Objekt platzieren',
callback() {
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const cart = new Cartesian2(x, y);
setupModelFunction(app, cart, url, name, false);
},
}
]
};
items.value.push(item);
addModelToObjectList(name, url);
},
// 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." +
......@@ -355,7 +324,7 @@ function uploadModel() {
timeout: 20000,
});
}, options);
}, undefined, function(error) {
}, 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." +
......@@ -363,6 +332,33 @@ function uploadModel() {
timeout: 20000,
});
});
}
function uploadModel() {
// Create an input element
const inputElement = document.createElement("input");
// Set its type to file
inputElement.type = "file";
inputElement.setAttribute("multiple", "");
// Set accept to the file types you want the user to select.
// Include both the file extension and the mime type
inputElement.accept = ".glb, .gltf";
// set onchange event to call callback when user has selected file
inputElement.addEventListener("change", e => {
const fileInput = e.target;
for (const file of fileInput.files) {
const reader = new FileReader();
reader.onload = async readerEvent => {
const content = readerEvent.target.result;
let blob = new Blob([content], {
type: "application/octet-stream",
});
let url = URL.createObjectURL(blob);
loadModel(url, file.name);
}
reader.readAsArrayBuffer(file);
}
......@@ -374,15 +370,15 @@ function uploadModel() {
}
function getRandomIcon() {
function getRandomIcon() {
const keys = Object.keys(Icons);
const index = Math.floor(keys.length * Math.random());
return `$${keys[index]}`;
}
}
export const objectListId = "object_list_id";
export const objectListId = "object_list_id";
export default {
export default {
name: 'objectList',
components: {
VcsListItemComponent,
......@@ -403,6 +399,7 @@ function uploadModel() {
VContainer,
VRow,
VCol,
VList,
VExpansionPanels,
},
setup() {
......@@ -431,7 +428,7 @@ function uploadModel() {
const dialog = ref(false);
setupModelFunction = setupModel;
projectStateObject = pluginState;
onMounted(()=>loadCatalogsFromConfig(config).then(() => {console.log("Object library loaded")}));
onMounted(() => loadCatalogsFromConfig(config).then(() => { console.log("Object library loaded") }));
return {
draggable,
selectable,
......@@ -537,12 +534,23 @@ function uploadModel() {
};
},
};
};
</script>
<style lang="scss" scoped>
.d-grid {
.d-grid {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
.no-scroll {
max-height: none; /* remove height limit */
overflow: visible; /* show all content */
padding-top: 0;
padding-bottom: 0;
}
.list-col {
height: auto; /* let column grow with content */
min-height: 0; /* reset flex min-height */
padding: 0; /* remove top/bottom padding if necessary */
}
</style>
......@@ -9,7 +9,7 @@
</VcsLabel>
</v-col>
<v-col>
<VcsTextField id="fileInput" type="file" v-model="state.files" />
<VcsTextField id="fileInput" type="file" @change="onFileSelected" />
</v-col>
</v-row>
<v-row no-gutters class="justify-center">
......@@ -39,9 +39,15 @@ import {
import { VContainer, VRow, VForm, VCol } from 'vuetify/components';
import { name } from '../package.json';
import ConverterStatus from "./converterStatus.vue";
import {loadModel} from "./objectList.vue";
export const bimOptionsId = 'upload_ifc_id';
const errorMessage = "Ein Fehler ist beim konvertieren aufgetreten, die Konvertierung unterstützt " +
"Dateien im IFC Format IFC2x3 und IFC4 Add2 TC1. Bitte stellen Sie sicher, dass die Datei " +
"die korrekte Version hat und nicht korrupt ist. Falls dies der Fall ist dann ist ein " +
"unbekannter Fehler beim Konvertieren aufgetreten, der nicht behoben werden konnte."
export default {
name: 'IFC Konvertierung',
components: {
......@@ -65,19 +71,22 @@ export default {
const app = inject('vcsApp');
const { pluginState, config } = app.plugins.getByKey(name);
const disable = ref(false);
const onFileSelected = (event) => {
pluginState.file = event.target.files[0];
}
return {
closeSelf() {
emit('close');
},
onFileSelected,
convert() {
const fileInput = document.getElementById("fileInput");
const fd = new FormData();
fd.append('file', pluginState.files);
fd.append('file', pluginState.file);
const req = fetch(config.convertLink, {
method: 'post',
body: fd /* or aFile[0]*/
}); // returns a promise
let filename = pluginState.files.name.replace(/\.[^/.]+$/, "") + ".glb";
let filename = pluginState.file.name.replace(/\.[^/.]+$/, "") + ".glb";
disable.value = true;
const convertBtn = document.getElementById("convertBtn");
convertBtn.innerHTML = " Konvertierung läuft ";
......@@ -86,6 +95,11 @@ export default {
disable.value = false;
convertBtn.innerHTML = " Konvertieren ";
emit('close');
const geolocationString = res.headers.get('X-geolocation');
if (geolocationString) {
const geolocation = JSON.parse(geolocationString);
pluginState.geolocations[filename] = geolocation;
}
if (res.ok) {
// status code was 200-299
app.notifier.add({
......@@ -97,20 +111,14 @@ export default {
} else {
app.notifier.add({
type: NotificationType.ERROR,
message: "Ein Fehler ist beim konvertieren aufgetreten, die Konvertierung unterstützt " +
"Dateien im IFC Format IFC2x3 und IFC4 Add2 TC1. Bitte stellen Sie sicher, dass die Datei " +
"die korrekte Version hat und nicht korrupt ist. Falls dies der Fall ist dann ist ein " +
"unbekannter Fehler beim Konvertieren aufgetreten, der nicht behoben werden konnte.",
message: errorMessage,
});
}
}, function (error) {
app.notifier.add({
type: NotificationType.ERROR,
message: "Ein Fehler ist beim konvertieren aufgetreten, die Konvertierung unterstützt " +
"Dateien im IFC Format IFC2x3 und IFC4 Add2 TC1. Bitte stellen Sie sicher, dass die Datei " +
"die korrekte Version hat und nicht korrupt ist. Falls dies der Fall ist dann ist ein " +
"unbekannter Fehler beim Konvertieren aufgetreten, der nicht behoben werden konnte.",
message: errorMessage,
});
}).then((blob) => {
if (blob != null) {
......@@ -121,6 +129,7 @@ export default {
document.body.appendChild(a);
a.click();
a.remove();
loadModel(url, filename);
}
})
},
......
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