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