Skip to content

Commit

Permalink
Merging latest features to production (#2574)
Browse files Browse the repository at this point in the history
* fix: order of async fetched dataInfo

* Integration of eox-map for the map widget (#2573)

* feat: initial eox-map swapping of ol

* chore: trying out exporting map config state in iframe text

* chore working on getting xyz layers working

* chore: changed update function

* fix: time update for XYZ layers

* chore: fix

* feat: implemented config export feature for map

* feat: added different export types for eox-map story blocks

* chore: upgrading story and jsonform packages, using new markdown placeholder for story editor

* chore: removed storytelling example

* chore: adding unique identifiers to data layers

* feat: exporting only layers without group as it has issues with eox-map, also simplifies code block; updated eox-map version

* chore: adding export of opacity in config when it is not set to 1

* chore: removing log message

---------

Co-authored-by: Lubomir Dolezal <[email protected]>

---------

Co-authored-by: Lubomir Dolezal <[email protected]>
  • Loading branch information
santilland and lubojr authored May 24, 2024
1 parent 77f7b78 commit a42bb9a
Show file tree
Hide file tree
Showing 11 changed files with 718 additions and 259 deletions.
258 changes: 237 additions & 21 deletions app/package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
"dependencies": {
"@chenfengyuan/vue-countdown": "^1.1.5",
"@eox/itemfilter": "0.14.0",
"@eox/jsonform": "^0.6.1",
"@eox/jsonform": "^0.7.0",
"@eox/layercontrol": "^0.17.3",
"@eox/stacinfo": "^0.3.3",
"@eox/map": "^1.6.1",
"@eox/storytelling": "^0.5.0",
"@eox/map": "^1.8.2",
"@eox/storytelling": "^1.0.2",
"@turf/difference": "^6.5.0",
"axios": "^0.21.1",
"chart.js": "2.9.3",
Expand Down
237 changes: 237 additions & 0 deletions app/src/components/OLExportButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<template>
<v-dialog
v-model="dialog"
width="500"
>
<template v-slot:activator="{}">
<template v-if="mapControl">
<v-tooltip left>
<template v-slot:activator="{ on: tooltip }">
<v-btn
:color="$vuetify.theme.currentTheme.background"
small
class="dashboard-button"
style="min-width: 0;"
v-on="{ ...tooltip, ...dialog }"
@click="dialog = true"
>
<v-icon>
mdi-map-plus
</v-icon>
</v-btn>
</template>
<span>Embed this map into a story</span>
</v-tooltip>
</template>
<v-btn
v-else
color="primary"
text
small
@click="dialog = true"
>
<template>
<v-icon left>mdi-map-plus</v-icon>
embed chart
</template>
</v-btn>
</template>

<v-card>
<v-card-title class="headline primary white--text">
Storytelling map configuration
</v-card-title>

<v-card-text class="py-5">
<div>
Copy and paste this code into the map layers field of the storytelling editor:
</div>
<div
class="pa-3"
style="background-color: #ddd;font-family: monospace;font-size: 11px;">
{{ layersConfig }}
</div>
<div style="position: absolute; bottom: 15px;">
<v-expand-transition>
<div v-if="copySuccess" class="success--text mr-3">
<v-icon
color="success"
left
>mdi-clipboard-check-outline</v-icon>
<small>copied!</small>
</div>
</v-expand-transition>
</div>
<div class="d-flex align-center justify-end pt-3">
<v-btn
small
text
@click="copy(layersConfig)"
>
<v-icon left>mdi-content-copy</v-icon>
copy as layers config
</v-btn>
</div>
<div class="d-flex align-center justify-end pt-3">
<v-btn
small
text
@click="copy(mapEntryCode)"
>
<v-icon left>mdi-content-copy</v-icon>
copy as simple map
</v-btn>
</div>
<div class="d-flex align-center justify-end pt-3">
<v-btn
small
text
@click="copy(mapStepCode)"
>
<v-icon left>mdi-content-copy</v-icon>
copy as map tour section
</v-btn>
</div>
</v-card-text>

<v-divider></v-divider>

<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="primary"
text
@click="dialog = false"
>
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>

<script>
import dialogMixin from '@/mixins/dialogMixin';
import {
mapState,
} from 'vuex';
import { getUid } from 'ol/util';
import { toLonLat } from 'ol/proj';
export default {
mixins: [dialogMixin],
props: {
indicatorObject: Object,
featureObject: Object,
mapControl: Boolean,
center: Object,
zoom: Number,
},
data: () => ({
dialog: false,
copySuccess: false,
}),
computed: {
...mapState('config', [
'appConfig',
]),
layersConfig() {
const layerConfig = this.extractLayerConfig(
this.$parent.$refs.mapContainer.map.getLayers().getArray(),
);
// remove internal layer group
layerConfig.splice(-1);
// reverse to use same order as eox-map config
layerConfig.reverse();
return JSON.stringify(layerConfig.flat());
},
mapStepCode() {
const mapView = this.$parent.$refs.mapContainer.map.getView();
const origCoords = mapView.get('center');
const lonlat = toLonLat(origCoords, mapView.getProjection());
const preTag = '### <!--{ layers=';
const endTag = `zoom="${mapView.get('zoom')}" center=[${[lonlat]}] animationOptions={duration:500}}-->
#### Tour step title
Text describing the current step of the tour and why it is interesting what the map shows currently
`;
return `${preTag}'${this.layersConfig}' ${endTag}`;
},
mapEntryCode() {
const mapView = this.$parent.$refs.mapContainer.map.getView();
const origCoords = mapView.get('center');
const lonlat = toLonLat(origCoords, mapView.getProjection());
const preTag = '## Map Example <!--{as="eox-map" style="width: 100%; height: 500px;" layers=';
const endTag = `zoom="${mapView.get('zoom')}" center=[${[lonlat]}] }-->`;
return `${preTag}'${this.layersConfig}' ${endTag}`;
},
},
methods: {
extractLayerConfig(layerArray) {
// Extract completely flat layers array without groups
const layers = [];
layerArray.map((l) => {
if (l.constructor.name.includes('Group')) {
layers.push(this.extractLayerConfig(l.getLayersArray()));
} else if (l.constructor.name.includes('STACLayer')) {
layers.push(l.get('_jsonDefinition'));
} else {
const layerConfig = {
type: l.constructor.name.replace('Layer', ''),
properties: {
id: l.get('configId') ? l.get('configId') : getUid(l),
},
};
// Evaluate what other information we need to extract for different source types
const olsource = l.getSource();
// only export visible layers
if (olsource && l.isVisible()) {
// Extract source config
const source = {
type: l.getSource().constructor.name.replace('Source', ''),
};
if (['XYZ', 'TileWMS', 'WMS'].includes(olsource.constructor.name)) {
if ('url' in olsource) {
source.url = olsource.url;
} else if ('urls' in olsource) {
source.urls = olsource.urls;
}
} else if (olsource.constructor.name === 'VectorSource') {
source.url = olsource.getUrl();
source.format = olsource.getFormat()?.constructor.name;
}
// Extract possible other configuration options
if (['TileWMS', 'WMS'].includes(olsource.constructor.name)) {
source.params = olsource.getParams();
source.serverType = olsource.serverType_;
}
if (olsource.constructor.name === 'VectorSource') {
// TODO: the getStyle function does not return the applied style as described in OL docs
layerConfig.style = ''; // l.getStyle();
}
if (l.getOpacity() !== 1) {
layerConfig.opacity = l.getOpacity();
}
layerConfig.source = source;
layers.push(layerConfig);
}
}
return true;
});
return layers;
},
async copy(s) {
await navigator.clipboard.writeText(s);
this.copySuccess = true;
},
},
};
</script>
<style scoped>
.dashboard-button {
width: 36px;
height: 36px !important;
z-index: 2;
pointer-events: initial;
}
</style>
45 changes: 25 additions & 20 deletions app/src/components/StacInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
<v-expansion-panels
style="justify-content: left;"
v-if="appConfig.id === 'gtif' && additionalGTIFDataInfos.length > 0"
:key="refreshKey"
>
<h4>
Dataset metadata
Expand Down Expand Up @@ -191,6 +192,7 @@ export default {
stacInfoLoaded: null,
themesInStacInfo: [],
linksInStacInfo: [],
refreshKey: 0,
}),
computed: {
...mapState('config', [
Expand Down Expand Up @@ -259,28 +261,31 @@ export default {
this.stacInfoLoaded = true;
});
},
getAdditionalGTIFDataInfos() {
this.additionalGtifDataInfoContent = [];
for (let i = 0; i < this.additionalGTIFDataInfos.length; i++) {
try {
const markdownUrl = `//raw.githubusercontent.com/eurodatacube/eodash-assets/main/collections/gtif-datainfo/${this.additionalGTIFDataInfos[i].dataInfo}.md`;
fetch(markdownUrl)
.then((response) => {
if (!response.ok) {
console.error('Fetching DataInfo failed');
}
return response.text();
})
.then((text) => {
const markdown = { default: text };
this.additionalGtifDataInfoContent.push(this.$marked(markdown.default));
});
} catch {
// just an empty catch to "fill in empty content"
this.additionalGtifDataInfoContent.push('');
}
async fetchDataInfo(dataInfoObject, i) {
try {
const markdownUrl = `//raw.githubusercontent.com/eurodatacube/eodash-assets/main/collections/gtif-datainfo/${dataInfoObject.dataInfo}.md`;
await fetch(markdownUrl)
.then((response) => {
if (!response.ok) {
console.error('Fetching DataInfo failed');
}
return response.text();
})
.then((text) => {
const markdown = { default: text };
this.additionalGtifDataInfoContent[i] = this.$marked(markdown.default);
});
} catch {
// just an empty catch
}
},
getAdditionalGTIFDataInfos() {
this.additionalGtifDataInfoContent = Array(this.additionalGTIFDataInfos.length).fill('');
Promise.all(this.additionalGTIFDataInfos.map((item, i) => this.fetchDataInfo(item, i)))
.then(() => {
this.refreshKey = Math.random();
});
},
},
};
</script>
Expand Down
Loading

0 comments on commit a42bb9a

Please sign in to comment.