<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=""> <details id="detail-prompt-examples-array" :open="detailIsOpenRef"> <summary><i>Expand this detail element for some prompt examples</i></summary> <div class="grid grid-cols-1 md:grid-cols-3" id="prompt-examples-array"> <div class="text-xs font-extralight flex bg-green-200"> <textarea id="prompt-text-placeholder" v-model="promptTextPlaceholderRef" class="p-2 border-2 border-indigo-500/100 w-full" /> </div> <StringArray :string-array="promptTextArray" :string-prefix="promptTextPlaceholderRef" @set-prompt="(stringPrompt: string) => promptTextRef = stringPrompt" /> </div> </details> <div class="flex"> <textarea id="prompt-text-llm-ref" v-model="promptTextRef" :placeholder=promptTextPlaceholderRef rows="2" class="w-full md:pt-1 md:pb-1 flex border-2 mt-2 mb-2 mr-2 border-indigo-500/100" ></textarea> <div class="w-full md:pt-1 md:pb-1 flex"> <ButtonMapSendStringRequest id="id-button-submit" class="h-8 mt-2 text-sm font-extralight min-w-[180px] max-w-[180px]" :current-base-map-name="currentBaseMapNameRef" :map="map" :promptText="promptTextRef" :response-message="responseMessageRef" :send-m-l-request="sendMLStringRequest" :waiting-string="waitingString" /> </div> </div> <div id="map" class="map-predictions"/> </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} ]"/> </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> </div> </div> </div> </template> <script lang="ts" setup> import { control as LeafletControl, Evented as LEvented, geoJSON as LeafletGeoJSON, type LatLng, Map as LMap, map as LeafletMap, tileLayer, TileLayer as LTileLayer } from 'leaflet' import 'leaflet-providers' import {onMounted, ref, type Ref} from 'vue' import {driver} from "../../node_modules/driver.js/src/driver" import { durationRef, numberOfPolygonsRef, numberOfPredictedMasksRef, OpenStreetMap, prefix, promptPlaceHolder, promptTextArray, responseMessageRef, Satellite, waitingString } from './constants' import { getExtentCurrentViewMapBBox, getGeoJSONRequest, getQueryParams, getSelectedPointCoordinate, updateMapData } from '@/components/helpers' import type {IBodyLatLngWithStringPoints, SourceTileType} from '@/components/types'; import StatsGrid from '@/components/StatsGrid.vue'; import StringArray from '@/components/StringArray.vue'; import ButtonMapSendStringRequest from '@/components/buttons/ButtonMapSendStringRequest.vue'; const lisaDriverObj = driver({ showProgress: true, steps: [ { element: 'id-prediction-map-container', popover: { title: 'SamGIS with LISA', description: 'A quick tour about functionalities of SamGIS with LISA', onNextClick: () => { detailIsOpenRef.value = true lisaDriverObj.moveNext(); } } }, { element: '#map', popover: { title: 'Geographic map', description: 'Choose here the map area where you can execute your machine learning prompt' } }, { element: "#prompt-examples-array", popover: {title: 'Some prompt examples', description: 'A selection of prompt examples'} }, { element: "#prompt-text-placeholder", popover: { title: 'Default prompt prefix', description: 'A good LLM prompt prefix tailored for photogrammetry and remote sensing (editable)' } }, { element: "#prompt-text-llm-ref", popover: { title: 'Prompt text', description: 'Editable text area for the LLM text prompt (you can precompile it clicking on the examples)' } }, { element: "#id-button-submit", popover: {title: 'LLM submit button', description: 'submit button for the LISA request'} }, { element: '.leaflet-control-layers-toggle', popover: {title: 'Map provider selector', description: 'select a different map provider'} }, { element: '#id-map-info', popover: { title: 'map info', description: 'Section about various map info', onNextClick: () => { detailIsOpenRef.value = false lisaDriverObj.moveNext(); } } }, { element: "#detail-prompt-examples-array", popover: { title: 'Detail: Array of Prompt Examples', description: 'Click here to show the array of prompt examples' } }, ] }); const currentBaseMapNameRef = ref("") const currentMapBBoxRef = ref() const currentZoomRef = ref() const promptTextRef: Ref<string> = ref("") const promptTextPlaceholderRef: Ref<string> = ref(promptPlaceHolder) const detailIsOpenRef: Ref<boolean> = ref(false) let map: LMap type ServiceTiles = { [key: SourceTileType]: LTileLayer; }; 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 } const sendMLStringRequest = async (leafletMap: LMap, promptRequest: string, sourceType: SourceTileType = OpenStreetMap) => { const bodyRequest: IBodyLatLngWithStringPoints = { bbox: getExtentCurrentViewMapBBox(leafletMap), string_prompt: promptRequest, zoom: leafletMap.getZoom(), source_type: sourceType } try { const geojsonOutputOnMounted = await getGeoJSONRequest(bodyRequest, '/infer_lisa') const featureNew = LeafletGeoJSON(geojsonOutputOnMounted) leafletMap.addLayer(featureNew) } catch (errGeojsonOutputOnMounted) { console.error('sendMLRequest:: sourceType: ', sourceType) console.error('sendMLRequest:: promptRequest: ', promptRequest.length, '::', promptRequest) console.error('sendMLRequest:: bodyRequest => ', bodyRequest, "#") console.error("errGeojsonOutputOnMounted => ", errGeojsonOutputOnMounted) } } const updateZoomBboxMap = (localMap: LMap) => { currentZoomRef.value = localMap.getZoom() currentMapBBoxRef.value = getExtentCurrentViewMapBBox(localMap) } const getCurrentBasemap = (url: string, providersArray: ServiceTiles) => { for (const [key, value] of Object.entries(providersArray)) { if (value._url == url) { return key } } } 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] }) map.fitBounds(props.mapBounds) map.attributionControl.setPrefix(prefix) LeafletControl.scale({position: 'bottomleft', imperial: false, metric: true}).addTo(map) LeafletControl.layers(baseMaps).addTo(map) updateZoomBboxMap(map) map.on('zoomend', (e: LEvented) => { updateZoomBboxMap(map) }) map.on('mouseup', (e: LEvented) => { currentMapBBoxRef.value = getExtentCurrentViewMapBBox(map) }) updateMapData(map, getPopupContentPoint, promptTextRef) map.on('baselayerchange', (e: LEvented) => { currentBaseMapNameRef.value = getCurrentBasemap(e.layer._url, baseMaps) }) lisaDriverObj.drive(); }) </script>