|
<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> |
|
<h2 id="lisa-response-text">LISA response: <span class="text-lg text-blue-600">{{ responseMessageLisaRef }}</span></h2> |
|
<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, |
|
responseMessageLisaRef, |
|
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, remote sensing and geomorphology (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: '#lisa-response-text', |
|
popover: {title: 'LISA response text', description: 'Output area with the LISA response text'} |
|
}, |
|
{ |
|
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 expand the array of prompt examples (closed by default)' |
|
} |
|
}, |
|
] |
|
}); |
|
|
|
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> |
|
|
|
|