| <template> | |
| <div class="h-auto" id="id-prediction-map-container"> | |
| <div class="grid grid-cols-1 2xl:grid-cols-5 lg:gap-1 lg:border-r ml-2 mt-2 md:ml-4 md:mr-4"> | |
| <div class="lg:border-r lg:col-span-3"> | |
| <div id="id-map-cont" class=""> | |
| <p class="hidden lg:block">{{ description }}</p> | |
| <div class="flex w-full md:pt-1 md:pb-1 lg:hidden portrait:xl:hidden"> | |
| <span class="flex portrait:hidden bg-slate-200 pl-2 pr-2">SamGIS demo</span> | |
| <ButtonMapSendArrayRequest | |
| id="id-button-submit" | |
| class="h-8 text-sm font-extralight min-w-[180px] max-w-[180px] ml-2 mr-2" | |
| :current-base-map-name="currentBaseMapNameRef" | |
| :map="map" | |
| :prompts-array="promptsArrayRef" | |
| :response-message="responseMessageRef" | |
| :send-m-l-request="sendMLArrayRequest" | |
| :waiting-string="waitingString" | |
| /> | |
| <span class="ml-2 lg:hidden"> | |
| <input type="checkbox" id="checkboxMapNavigationLocked" v-model="mapNavigationLocked" /> | |
| <span class="ml-2"> | |
| <label class="text-red-600" for="checkboxMapNavigationLocked" v-if="mapNavigationLocked">locked map!</label> | |
| <label class="text-blue-600" for="checkboxMapNavigationLocked" v-else>map unlocked</label> | |
| </span> | |
| </span> | |
| </div> | |
| <div id="map" class="map-predictions" /> | |
| <ButtonMapSendArrayRequest | |
| class="h-8 min-w-[240px] max-w-[240px] mt-2 mb-2 hidden sd:h-14 lg:block portrait:xl:block" | |
| :current-base-map-name="currentBaseMapNameRef" | |
| id="id-button-submit" | |
| :map="map" | |
| :prompts-array="promptsArrayRef" | |
| :response-message="responseMessageRef" | |
| :send-m-l-request="sendMLArrayRequest" | |
| :waiting-string="waitingString" | |
| /> | |
| <span class="hidden lg:block lg:ml-2"> | |
| <input type="checkbox" id="checkboxMapNavigationLocked" v-model="mapNavigationLocked" /> | |
| <span class="ml-2"> | |
| <label class="text-red-600" for="checkboxMapNavigationLocked" v-if="mapNavigationLocked">locked map navigation!</label> | |
| <label class="text-blue-600" for="checkboxMapNavigationLocked" v-else>map navigation unlocked</label> | |
| </span> | |
| </span> | |
| </div> | |
| </div> | |
| <div class="lg:col-span-2"> | |
| <div class="lg:pl-2 lg:pr-2 lg:border-l lg:border-3" id="id-map-info"> | |
| <h1>Map Info</h1> | |
| <div class="grid grid-cols-1 md:grid-cols-3"> | |
| <StatsGrid :stats-array="[ | |
| {statName: 'current Zoom', statValue: currentZoomRef}, | |
| {statName: 'current map name/type', statValue: currentBaseMapNameRef}, | |
| {statName: 'prompt: points/rectangles number', statValue: promptsArrayRef.length}, | |
| ]" /> | |
| </div> | |
| <div v-if="responseMessageRef === waitingString" /> | |
| <h2 v-else-if="responseMessageRef || responseMessageRef == '-'" class="text-lg text-red-600">{{ responseMessageRef }}</h2> | |
| <div v-else> | |
| <div class="grid grid-cols-1 md:grid-cols-3"> | |
| <StatsGrid :stats-array="[ | |
| {statName: 'request duration', statValue: `${durationRef.toFixed(2)}s`}, | |
| {statName: 'polygons number', statValue: numberOfPolygonsRef}, | |
| {statName: 'predicted masks number', statValue: numberOfPredictedMasksRef}, | |
| ]" /> | |
| </div> | |
| </div> | |
| </div> | |
| <h1 id="id-ml-request-prompt">ML request prompt</h1> | |
| <p>Exclude points: label 0, include points: label 1.</p> | |
| <div v-if="promptsArrayRef.filter(el => {return el.type === 'point'}).length > 0"> | |
| <TableGenericComponent | |
| :header="['id', 'data', 'label']" | |
| :rows="applyFnToObjectWithinArray(promptsArrayRef.filter(el => {return el.type === 'point'}))" | |
| title="Points" | |
| row-key="id" | |
| /> | |
| </div> | |
| <br /> | |
| <div v-if="promptsArrayRef.filter(el => {return el.type === 'rectangle'}).length > 0"> | |
| <TableGenericComponent | |
| :header="['id', 'data_ne', 'data_sw']" | |
| :rows="applyFnToObjectWithinArray(promptsArrayRef.filter(el => {return el.type === 'rectangle'}))" | |
| title="Rectangles" | |
| row-key="id" | |
| class="2md:min-h-[100px]" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <script lang="ts" setup> | |
| import { | |
| control as LeafletControl, | |
| Evented as LEvented, | |
| type LatLng, | |
| Map as LMap, | |
| map as LeafletMap, | |
| tileLayer, | |
| TileLayer as LTileLayer | |
| } from 'leaflet' | |
| import 'leaflet-providers' | |
| import '@geoman-io/leaflet-geoman-free' | |
| import { onMounted, onUpdated, ref, type Ref } from 'vue' | |
| // workaround because of dist/ content not included in @trincadev/driver.js tag release tarball | |
| import { driver } from "../../node_modules/@trincadev/driver.js/src/driver" | |
| import { | |
| currentBaseMapNameRef, | |
| currentMapBBoxRef, | |
| currentZoomRef, | |
| driverSteps, | |
| durationRef, | |
| layerControlGroupLayersRef, | |
| mapNavigationLocked, | |
| mapOptionsDefaultRef, | |
| maxZoom, | |
| minZoom, | |
| numberOfPolygonsRef, | |
| numberOfPredictedMasksRef, | |
| OpenStreetMap, | |
| prefix, | |
| promptsArrayRef, | |
| responseMessageRef, | |
| Satellite, | |
| waitingString | |
| } from './constants' | |
| import { | |
| applyFnToObjectWithinArray, | |
| getExtentCurrentViewMapBBox, | |
| sendMLArrayRequest, | |
| getQueryParams, | |
| getSelectedPointCoordinate, | |
| setGeomanControls, | |
| updateMapData, | |
| updateZoomBboxMap, | |
| getCurrentBasemap | |
| } from '@/components/helpers' | |
| import type { SourceTileType, ServiceTiles } from '@/components/types'; | |
| import StatsGrid from '@/components/StatsGrid.vue'; | |
| import TableGenericComponent from '@/components/TableGenericComponent.vue'; | |
| import ButtonMapSendArrayRequest from '@/components/buttons/ButtonMapSendArrayRequest.vue'; | |
| const driverObj = driver({ | |
| showProgress: true, | |
| steps: driverSteps | |
| }); | |
| let map: LMap | |
| const props = defineProps<{ | |
| mapBounds: Array<LatLng>, | |
| mapName: string, | |
| description: string | |
| }>() | |
| const getPopupContentPoint = (leafletEvent: LEvented, label: number): HTMLDivElement => { | |
| let popupContent: HTMLDivElement = document.createElement('div') | |
| let currentPointLayer: LatLng = getSelectedPointCoordinate(leafletEvent) | |
| popupContent.innerHTML = `<span>lat:${JSON.stringify(currentPointLayer.lat)}<br/>` | |
| popupContent.innerHTML += `lng:${JSON.stringify(currentPointLayer.lng)}<br/>` | |
| popupContent.innerHTML += `label:${label}, id:${leafletEvent.layer._leaflet_id}</span>` | |
| const popupDiv: HTMLDivElement = document.createElement('div') | |
| popupDiv.className = 'leaflet-popup-content-inner' | |
| popupDiv.appendChild(popupContent) | |
| return popupDiv | |
| } | |
| onMounted(async () => { | |
| const osmTile = tileLayer.provider(OpenStreetMap) | |
| const params = getQueryParams() | |
| let localVarSatellite: SourceTileType = params.source ? params.source : Satellite | |
| let localVarSatelliteOptions = params.options ? params.options : {} | |
| const satelliteTile = tileLayer.provider(localVarSatellite, localVarSatelliteOptions) | |
| let localVarTerrain: SourceTileType = "nextzen.terrarium" | |
| const terrainTile = new LTileLayer( | |
| "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png", { | |
| id: localVarTerrain, | |
| attribution: "<a href='https://nextzen.org'>nextzen</a>," + | |
| "<a href='https://registry.opendata.aws/terrain-tiles/'>Mapzen Terrain Tiles - AWS opendata registry</a>," + | |
| "<a href='https://github.com/tilezen/joerd/blob/master/docs/attribution.md'>Mapzen Source Attributions</a>." | |
| } | |
| ) | |
| let baseMaps: ServiceTiles = { OpenStreetMap: osmTile } | |
| baseMaps[localVarSatellite] = satelliteTile | |
| baseMaps[localVarTerrain] = terrainTile | |
| currentBaseMapNameRef.value = OpenStreetMap | |
| map = LeafletMap('map', { | |
| layers: [osmTile], | |
| minZoom: minZoom, | |
| maxZoom: maxZoom | |
| }) | |
| map.fitBounds(props.mapBounds) | |
| map.attributionControl.setPrefix(prefix) | |
| LeafletControl.scale({ position: 'bottomleft', imperial: false, metric: true }).addTo(map) | |
| layerControlGroupLayersRef.value = LeafletControl.layers(baseMaps).addTo(map) | |
| setGeomanControls(map) | |
| updateZoomBboxMap(map) | |
| mapOptionsDefaultRef.value = {...map.options} | |
| map.on('zoomend', (e: LEvented) => { | |
| updateZoomBboxMap(map) | |
| }) | |
| map.on('mouseup', (e: LEvented) => { | |
| currentMapBBoxRef.value = getExtentCurrentViewMapBBox(map) | |
| }) | |
| updateMapData(map, getPopupContentPoint, promptsArrayRef) | |
| map.on('baselayerchange', (e: LEvented) => { | |
| currentBaseMapNameRef.value = getCurrentBasemap(e.layer._url, baseMaps) | |
| }) | |
| driverObj.drive(); | |
| }) | |
| onUpdated(() => { | |
| if (mapNavigationLocked.value) { | |
| map.setMaxZoom(currentZoomRef.value) | |
| map.setMinZoom(currentZoomRef.value) | |
| map.options.maxBoundsViscosity = 1.0 | |
| map.setMaxBounds(map.getBounds()) | |
| } | |
| if (!mapNavigationLocked.value) { | |
| map.setMaxZoom(maxZoom) | |
| map.setMinZoom(minZoom) | |
| map.options.maxBoundsViscosity = 0.0 | |
| map.setMaxBounds([ | |
| [90, 180], | |
| [-90, -180] | |
| ]) | |
| } | |
| }) | |
| </script> | |