Skip to content

Commit

Permalink
Merge pull request #212 from IGNF/feature/layer-manager
Browse files Browse the repository at this point in the history
feat(menu) : gestion des menus autour de la carte
  • Loading branch information
IGNFhc authored May 28, 2024
2 parents 31710c4 + 60d96d7 commit 77ab915
Show file tree
Hide file tree
Showing 28 changed files with 1,238 additions and 200 deletions.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
"dependencies": {
"@gouvfr/dsfr": "^1.11.2",
"@gouvminint/vue-dsfr": "^5.14.2",
"@vueuse/components": "^10.9.0",
"@vueuse/core": "^10.9.0",
"geoportal-extensions-openlayers": "./geoportal-extensions-openlayers-1.0.0-beta.32-52.tgz",
"ol": "8.2.0"
"ol": "8.2.0",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@antfu/eslint-config": "^2.11.6",
Expand All @@ -37,6 +41,7 @@
"oh-vue-icons": "1.0.0-rc3",
"pinia": "^2.1.7",
"rimraf": "^5.0.5",
"sass": "^1.76.0",
"typescript": "~5.4.0",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.27.0",
Expand Down
72 changes: 44 additions & 28 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { useMatchMedia } from '@/composables/matchMedia';
import { useHeaderParams } from '@/composables/headerParams';
import { useFooterParams } from '@/composables/footerParams';
import StoreDataLoading from './components/StoreDataLoading.vue';
useScheme()
import { useModel } from 'vue';
Expand Down Expand Up @@ -85,36 +83,54 @@ const navItems: DsfrNavigationProps['navItems'] = [
</script>

<template>
<DsfrHeader v-model="headerParams.serviceTitle" :service-title="headerParams.serviceTitle"
:service-description="headerParams.serviceDescription" :logo-text="headerParams.logoText"
<DsfrHeader
v-model="headerParams.serviceTitle"
:service-title="headerParams.serviceTitle"
:service-description="headerParams.serviceDescription"
:logo-text="headerParams.logoText"
:quick-links="headerParams.quickLinks">
<DsfrNavigation :nav-items="navItems" v-show="!largeScreen" />
<DsfrNavigation
:nav-items="navItems"
v-show="!largeScreen" />
</DsfrHeader>

<Suspense>
<StoreDataLoading />
<!-- loading state -->
<template #fallback>
Loading...
</template>
</Suspense>


<div>
<router-view />
</div>

<DsfrFooter :before-mandatory-links="footerParams.beforeMandatoryLinks" :after-mandatory-links="afterMandatoryLinks"
:a11y-compliance="footerParams.a11yCompliance" :logo-text="footerParams.logoText" :legal-link="footerParams.legalLink"
:personal-data-link="footerParams.personalDataLink" :cookies-link="footerParams.cookiesLink"
:a11y-compliance-link="footerParams.a11yComplianceLink" :desc-text="footerParams.descText"
:home-link="footerParams.homeLink" :partners="footerParams.partners" :licence-text="footerParams.licenceText"
:licence-to="footerParams.licenceTo" :licence-name="footerParams.licenceName"
:licence-link-props="footerParams.licenceLinkProps" :ecosystem-links="footerParams.ecosystemLinks" />
<div class="fr-container fr-container--fluid fr-container-md">
<DsfrModal ref="modal" :opened="themeModalOpened" :title="footerParams.themeModale.title"
:size="footerParams.themeModale.size" @close="onModalClose">
<DsfrRadioButtonSet v-model="modelValue" :legend="footerParams.themeModale.legend" name="fr-radios-theme"
:options="footerParams.themeModale.themeOptions" @update:model-value="changeTheme" />
</DsfrModal>
</div>
<DsfrFooter
:before-mandatory-links="footerParams.beforeMandatoryLinks"
:after-mandatory-links="afterMandatoryLinks"
:a11y-compliance="footerParams.a11yCompliance"
:logo-text="footerParams.logoText"
:legal-link="footerParams.legalLink"
:personal-data-link="footerParams.personalDataLink"
:cookies-link="footerParams.cookiesLink"
:a11y-compliance-link="footerParams.a11yComplianceLink"
:desc-text="footerParams.descText"
:home-link="footerParams.homeLink"
:partners="footerParams.partners"
:licence-text="footerParams.licenceText"
:licence-to="footerParams.licenceTo"
:licence-name="footerParams.licenceName"
:licence-link-props="footerParams.licenceLinkProps"
:ecosystem-links="footerParams.ecosystemLinks" />

<div class="fr-container fr-container--fluid fr-container-md">
<DsfrModal
ref="modal"
:opened="themeModalOpened"
:title="footerParams.themeModale.title"
:size="footerParams.themeModale.size"
@close="onModalClose">

<DsfrRadioButtonSet
v-model="modelValue"
:legend="footerParams.themeModale.legend"
name="fr-radios-theme"
:options="footerParams.themeModale.themeOptions"
@update:model-value="changeTheme" />
</DsfrModal>
</div>

</template>
16 changes: 15 additions & 1 deletion src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,29 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
Attributions: typeof import('./components/carte/control/Attributions.vue')['default']
Carto: typeof import('./components/carte/Carto.vue')['default']
Control: typeof import('./components/carte/Control.vue')['default']
DsfrCheckboxSet: typeof import('@gouvminint/vue-dsfr')['DsfrCheckboxSet']
DsfrFooter: typeof import('@gouvminint/vue-dsfr')['DsfrFooter']
DsfrHeader: typeof import('@gouvminint/vue-dsfr')['DsfrHeader']
DsfrModal: typeof import('@gouvminint/vue-dsfr')['DsfrModal']
DsfrNavigation: typeof import('@gouvminint/vue-dsfr')['DsfrNavigation']
DsfrRadioButtonSet: typeof import('@gouvminint/vue-dsfr')['DsfrRadioButtonSet']
DsfrSideMenu: typeof import('@gouvminint/vue-dsfr')['DsfrSideMenu']
Layer: typeof import('./components/carte/Layer/Layer.vue')['default']
LayerManager: typeof import('./components/carte/Layer/LayerManager.vue')['default']
LayerSwitcher: typeof import('./components/carte/control/LayerSwitcher.vue')['default']
LeftMenu: typeof import('./components/menu/LeftMenu.vue')['default']
Map: typeof import('./components/carte/Map.vue')['default']
MenuControl: typeof import('./components/carte/MenuControl.vue')['default']
MenuCatalogue: typeof import('./components/menu/MenuCatalogue.vue')['default']
MenuControl: typeof import('./components/menu/MenuControl.vue')['default']
MenuLateralContent: typeof import('./components/menu/MenuLateralContent.vue')['default']
MenuLateralNavButton: typeof import('./components/menu/MenuLateralNavButton.vue')['default']
MenuLateralWrapper: typeof import('./components/menu/MenuLateralWrapper.vue')['default']
MenuObject: typeof import('./components/menu/MenuObject.vue')['default']
OverviewMap: typeof import('./components/carte/control/OverviewMap.vue')['default']
Patience: typeof import('./components/utils/Patience.vue')['default']
RightMenu: typeof import('./components/menu/RightMenu.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ScaleLine: typeof import('./components/carte/control/ScaleLine.vue')['default']
Expand Down
14 changes: 5 additions & 9 deletions src/components/StoreDataLoading.vue
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
<script setup lang="js">
import { useLogger } from 'vue-logger-plugin'
import { storeToRefs } from 'pinia'
import { useDataStore } from "@/stores/dataStore"
const props = defineProps({})
const log = useLogger()
const store = useDataStore()
const { getLayers } = storeToRefs(store)
const storeData = useDataStore()
// INFO
// l'opération est asynchrone, il faut donc attendre que le store soit chargé
// avant d'initialiser la carte.
await store.fetchData()
if (store.isLoaded) {
log.debug(toRaw(getLayers.value))
}
const { fetchData } = storeData;
const res = await fetchData();
log.debug(res);
</script>

<template>
<!-- TODO mettre en place une patience -->
<slot v-if="storeData.isLoaded && !storeData.error"></slot>
</template>

<style scoped></style>
37 changes: 37 additions & 0 deletions src/components/carte/Carto.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script setup lang="js">
import Map from '@/components/carte/Map.vue'
import View from '@/components/carte/View.vue'
import Control from '@/components/carte/Control.vue'
import LayerManager from '@/components/carte/Layer/LayerManager.vue'
import { useMapStore } from "@/stores/mapStore"
const props = defineProps({
selectedControls : Array,
layersList : Object,
mapWidth: Number
})
const mapStore = useMapStore()
</script>

<template>
<Map
:width="props.mapWidth">
<View
:center="mapStore.center"
:zoom="mapStore.zoom"/>
<Control
v-if="selectedControls"
:control-options="props.selectedControls"/>
<!-- FIXME c'est un composant pour l'exemple
donc provisoire ! -->
<LayerManager
:layers-list="props.layersList"/>
</Map>
</template>

<style scoped>
</style>
16 changes: 16 additions & 0 deletions src/components/carte/Control.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,24 @@ import ScaleLine from './control/ScaleLine.vue'
import OverviewMap from './control/OverviewMap.vue'
import Zoom from './control/Zoom.vue'
import Attributions from './control/Attributions.vue'
import LayerSwitcher from './control/LayerSwitcher.vue'
import { useControls } from '@/composables/controls'
const props = defineProps({
controlOptions: Array
})
const layerSwitcherOptions = {
options : {
// FIXME
// position : "top-right",
collapsed : true,
panel : true,
counter : true
}
}
const scaleLineOptions = {
units: 'metric',
bar: false,
Expand All @@ -31,6 +42,7 @@ const searchEngineOptions = {
search: true
},
searchOptions: {
addToMap : false,
serviceOptions : {
services: "WMTS,WMS,TMS"
}
Expand All @@ -55,6 +67,10 @@ const attributionsOptions = {}
</script>

<template>
<LayerSwitcher
:visibility="props.controlOptions.includes(useControls.LayerSwitcher)"
:layer-switcher-options="layerSwitcherOptions"
/>
<Zoom
:visibility="props.controlOptions.includes(useControls.Zoom)"
:zoom-options="zoomOptions"
Expand Down
28 changes: 28 additions & 0 deletions src/components/carte/Layer/Layer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script setup lang="js">
// FIXME c'est pour l'exemple car les couches sont gérées par les extensions !
// cf. LayerManager
import TileLayer from 'ol/layer/Tile.js'
const props = defineProps({
layerOptions: Object
})
const map = inject('map')
const layer = ref(new TileLayer(props.layerOptions))
onMounted(() => {
map?.addLayer(layer.value)
})
onUnmounted(() => {
map?.removeLayer(layer.value)
})
</script>
<template>
<div>
<slot></slot>
</div>
</template>
49 changes: 49 additions & 0 deletions src/components/carte/Layer/LayerManager.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="js">
// FIXME c'est juste pour l'exemple car on va ajouter l'extension LayerSwitcher !
// Donc provisoire...
import Layer from '@/components/carte/Layer/Layer.vue'
import { resolutions } from '@/composables/layers'
import WMTS from 'ol/source/WMTS.js';
import WMTSTileGrid from 'ol/tilegrid/WMTS';
const props = defineProps({
layersList: Object
})
var layersConfList = computed(() => {return toRaw(props.layersList).map(layername => addWMTSLayer(layername))});
function addWMTSLayer(layer) {
if (layer && Object.keys(layer).length) {
const startRes = Object.keys(layer.wmtsOptions.tileMatrixSetLimits)[0]
const endRes = Object.keys(layer.wmtsOptions.tileMatrixSetLimits).slice(-1)
const sourceOptions = {
url: 'https://wmts.geopf.fr/wmts',
layer: layer.name,
matrixSet: layer.wmtsOptions.tileMatrixSetLink,
projection: layer.defaultProjection,
format: layer.formats[0].name,
style: layer.styles[0].name,
tileGrid : new WMTSTileGrid({
origin: [-20037508,20037508], // topLeftCorner
resolutions: resolutions.slice(startRes, endRes), // résolutions
matrixIds: Object.keys(layer.wmtsOptions.tileMatrixSetLimits) // ids des TileMatrix
})
};
var source = new WMTS(sourceOptions);
source._title = layer.name;
return {
source : source,
opacity: 1
}
}
else return {}
}
</script>

<template>
<Layer
v-for="layer in layersConfList"
:layer-options="layer"
/>
</template>
3 changes: 1 addition & 2 deletions src/components/carte/Map.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ provide('map', map)

<style>
#map {
margin-left: 0;
width: inherit;
height: 70vh;
height: inherit;
}
</style>
13 changes: 4 additions & 9 deletions src/components/carte/View.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const store = useMapStore()
const props = defineProps({
zoom : Number,
center : Array,
layers : Array
})
// recuperation de l'objet 'map' du composant parent
Expand All @@ -26,27 +25,23 @@ const view = new View({
* pour mise à jour du centre de la carte
*/
view.on("change:center", (e) => {
store.setCenter(e.target.getCenter());
store.lat = e.target.getCenter()[0]
store.long = e.target.getCenter()[1]
})
/**
* abonnement à l'evenement 'change:resolution' de la vue
* pour mise à jour du zoom de la carte
*/
view.on("change:resolution", (e) => {
store.setZoom(view.getZoom())
store.zoom = view.getZoom()
})
onMounted(() => {
if (map) {
// ajout des couches
props.layers.forEach((layer) => {
map.addLayer(layer)
})
// ajout de la vue
map.setView(view)
// enregistrement
store.setMap(map)
store.map = map
}
})
</script>
Expand Down
Loading

0 comments on commit 77ab915

Please sign in to comment.