Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions src/components/generic/maplibre-wrapper/MaplibreWrapper.scss

This file was deleted.

40 changes: 34 additions & 6 deletions src/components/generic/maplibre-wrapper/MaplibreWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<a class="maptiler-link" href="https://www.maptiler.com/" target="_blank">
<img src="/static/maptiler-logo.svg" />
</a>
<!-- Maplibre map will be injected here -->
<!-- Map will be displayed here -->
</div>
</template>

<script>
import { onMounted, watchEffect } from "vue";
import { useMaplibre } from "../../../composables/maplibre.js";
import { MaplibreMap } from "../../../utils/maplibre.js";

export default {
props: {
Expand All @@ -27,16 +27,44 @@ export default {
}
},
setup(props) {
const { loadMap } = useMaplibre(props.containerID);

onMounted(() => {
watchEffect(() => {
loadMap(props.longitude, props.latitude);
// Do not try to load map if container element does not exist
if (!document.getElementById(props.containerID)) return;

const center = [props.longitude, props.latitude];
const map = new MaplibreMap(props.containerID, center);
map.addMarker(center);
});
});
}
};
</script>

<style src="../../../../node_modules/maplibre-gl/dist/maplibre-gl.css"></style>
<style scoped lang="scss" src="./MaplibreWrapper.scss"></style>
<style scoped>
.maplibre-wrapper {
position: relative;
width: 100%;
height: 100%;
filter: grayscale(0.7);

.maptiler-link {
position: absolute;
z-index: 1;
bottom: calc(var(--spacing-unit) / 4);
left: calc(var(--spacing-unit) / 2);
user-select: none;
}

&:deep() {
.maplibregl-canvas {
outline: none;
}
.maplibregl-canvas-container {
width: 100%;
height: 100%;
}
}
}
</style>
94 changes: 94 additions & 0 deletions src/components/specific/models/models-map/ModelsMap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<template>
<div class="models-map" :id="CONTAINER_ID">
<!-- Map will be displayed here -->
</div>
</template>

<script setup>
import { onBeforeUnmount, onMounted, watch } from "vue";
import { useRouter } from "vue-router";
import { useModels } from "../../../../state/models.js";
import { computeBounds, MaplibreMap } from "../../../../utils/maplibre.js";
import { DMS2DD } from "../../../../utils/location.js";
import { openInViewer } from "../../../../utils/models.js";

const router = useRouter();
const { fetchModelLocation } = useModels();

const CONTAINER_ID = "models-map";

const props = defineProps({
models: {
type: Array,
default: () => [],
},
});

const loadedModelIds = new Set();
const markers = new Map();

onMounted(() => {
const map = new MaplibreMap(CONTAINER_ID);

watch(
() => props.models,
async models => {
const ids = new Set(models.map(m => m.id));
const modelIdsToLoad = ids.difference(loadedModelIds);
const modelIdsToUnload = loadedModelIds.difference(ids);

await Promise.all(
models.map(async model => {
if (modelIdsToLoad.has(model.id)) {
const location = await fetchModelLocation(model.project, model);
const { longitude, latitude } = location;

if (longitude && latitude) {
const position = [DMS2DD(longitude, "longitude"), DMS2DD(latitude, "latitude")];
const marker = await map.addMarker(position);
marker.on("click", () => openInViewer(router, model.project, model));

markers.set(model.id, marker);
}

loadedModelIds.add(model.id);
}

if (modelIdsToUnload.has(model.id)) {
// TODO: remove marker from map

loadedModelIds.delete(model.id);
}
})
);

// Set map camera
if (markers.length === 0) {
return;
}
if (markers.length === 1) {
map.setCenter(markers[0]);
return;
}

const bounds = computeBounds([...markers.values()]);
if (bounds) map.map.fitBounds(bounds, { animate: false });
},
{ immediate: true }
);
});

onBeforeUnmount(() => {
loadedModelIds.clear();
markers.clear();
});
</script>

<style src="../../../../../node_modules/maplibre-gl/dist/maplibre-gl.css"></style>
<style scoped>
.models-map {
position: relative;
width: 100%;
height: 100%;
}
</style>
61 changes: 26 additions & 35 deletions src/components/specific/projects/project-card/ProjectCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
</template>

<script setup>
import { inject, onMounted, onUnmounted, ref, watch } from "vue";
import { inject, onMounted, onUnmounted, ref, toRaw } from "vue";
import { useRouter } from "vue-router";
import { useToggle } from "../../../../composables/toggle.js";
import { MODEL_TYPE } from "../../../../config/models.js";
Expand Down Expand Up @@ -118,6 +118,8 @@ const props = defineProps({
}
});

const emit = defineEmits(["loaded"]);

const router = useRouter();
const { isSpaceAdmin, isFavoriteProject } = useUser();
const { isOpen: showMenu, open: openMenu, close: closeMenu } = useToggle();
Expand All @@ -144,47 +146,36 @@ onMounted(() => {
([{ isIntersecting }]) => {
if (!isIntersecting) return;

setTimeout(() => {
setTimeout(async () => {
// Do not load card if it is not in the viewport
if (!isElementInViewport(placeholder.value, viewContainer.value.clientHeight)) return;

unwatchProject = watch(
() => props.project,
async () => {
loading.value = true;
const models = await ModelService.fetchModels(props.project, { cache: true });
displayedModels.value = models.reduce(
(acc, model) => {
if (
!model.archived &&
model.type !== MODEL_TYPE.META_BUILDING &&
model.type !== MODEL_TYPE.PHOTOSPHERE_BUILDING
) {
if (model.id === props.project.main_model_id) {
acc.unshift(model);
} else {
acc.push(model);
}
}
return acc;
},
[]
);
loading.value = false;
visible.value = true;
},
{ immediate: true }
);

unwatchModels = watch(
displayedModels,
() => {
currentModel.value = displayedModels.value[0];
loading.value = true;
const models = await ModelService.fetchModels(props.project);
displayedModels.value = models.reduce(
(acc, model) => {
if (
!model.archived &&
model.type !== MODEL_TYPE.META_BUILDING &&
model.type !== MODEL_TYPE.PHOTOSPHERE_BUILDING
) {
if (model.id === props.project.main_model_id) {
acc.unshift(model);
} else {
acc.push(model);
}
}
return acc;
},
{ immediate: true }
[]
);
currentModel.value = displayedModels.value[0];
loading.value = false;
visible.value = true;

observer.disconnect();

emit("loaded", { project: toRaw(props.project), models: displayedModels.value });
}, LOADING_DELAY);
},
{
Expand Down
86 changes: 86 additions & 0 deletions src/components/specific/projects/projects-map/ProjectsMap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<template>
<div class="projects-map" :id="CONTAINER_ID">
<!-- Map will be displayed here -->
</div>
</template>

<script setup>
import { onBeforeUnmount, onMounted } from "vue";
import { useRouter } from "vue-router";
import { useModels } from "../../../../state/models.js";
import routeNames from "../../../../router/route-names.js";
import { debounce } from "../../../../utils/async.js";
import { computeBounds, MaplibreMap } from "../../../../utils/maplibre.js";
import { DMS2DD } from "../../../../utils/location.js";

const router = useRouter();
const { fetchModelLocation } = useModels();

const CONTAINER_ID = "projects-map";

let map = null, markers;

const setMapCamera = debounce(() => {
if (markers.size === 0) {
return;
}
if (markers.size === 1) {
const marker = markers.values().next().value;
const center = marker.getLngLat();
map.setCenter(center);
return;
}

const bounds = computeBounds([...markers.values()]);
if (bounds) map.map.fitBounds(bounds, { animate: false });
}, 600);

const addProject = async project => {
if (!map) return;

for (const model of project.models) {
const location = await fetchModelLocation(project, model);
const { longitude, latitude } = location;

if (longitude && latitude) {
const position = [DMS2DD(longitude, "longitude"), DMS2DD(latitude, "latitude")];
const marker = await map.addMarker(position);
marker.on("click", () =>
router.push({
name: routeNames.projectBoard,
params: {
spaceID: model.project.cloud.id,
projectID: model.project.id,
}
})
);

markers.set(project.id, marker);
break;
}
}

setMapCamera();
};

defineExpose({ addProject });

onMounted(() => {
map = new MaplibreMap(CONTAINER_ID);
markers = new Map();
});

onBeforeUnmount(() => {
markers?.clear();
map?.map.remove();
});
</script>

<style src="../../../../../node_modules/maplibre-gl/dist/maplibre-gl.css"></style>
<style scoped>
.projects-map {
position: relative;
width: 100%;
height: 100%;
}
</style>
Loading
Loading