mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 09:27:07 +00:00
Implement map crosshairs.
These are coordinates that are supplied in the fragment part of the URL. When available, a marker is shown at the given positions. Labels may also be given and are also shown.
This commit is contained in:
@@ -305,6 +305,14 @@
|
||||
<label><!-- No lines available --></label>
|
||||
<label><!-- No heatmap available --></label>
|
||||
|
||||
<template v-if="crosshairsPositions.length">
|
||||
<span>Markers</span>
|
||||
<label title="Show crosshair markers"><v-icon small left class="mx-0">mdi-crosshairs</v-icon> <input type="checkbox" value="crosshairs" v-model="layerSelection"/></label>
|
||||
<label title="Show marker labels" v-if="crosshairsLabels.length"><v-icon small left class="mx-0">mdi-format-text-variant</v-icon> <input type="checkbox" value="labels" v-model="layerSelection"/></label>
|
||||
<label v-else><!-- No labels provided --></label>
|
||||
<label><!-- No heatmap available --></label>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
<!--
|
||||
<input id="lyr-psl" type="checkbox" value="psl" v-model="layerSelection"/>
|
||||
@@ -331,12 +339,6 @@
|
||||
<input id="lyr-nav" type="checkbox" value="nav" v-model="layerSelection"/>
|
||||
<label for="lyr-nav" title="Vessel track">Navigation trail</label>
|
||||
-->
|
||||
<form>
|
||||
<template v-if="crosshairsPosition.length">
|
||||
<input id="lyr-crosshairs" type="checkbox" value="crosshairs" v-model="layerSelection"/>
|
||||
<label for="lyr-crosshairs" title="Show or hide the crosshairs position marker">Crosshairs marker</label>
|
||||
</template>
|
||||
</form>
|
||||
|
||||
<!-- QC data: This section is meant to show (some) QC results in a graphical way,
|
||||
as 3D columns with lengths proportional to the QC values. Not implemented
|
||||
@@ -576,7 +578,7 @@ import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import zoomFitIcon from '@/assets/zoom-fit-best.svg'
|
||||
|
||||
import { Deck, FlyToInterpolator } from '@deck.gl/core';
|
||||
import { IconLayer } from '@deck.gl/layers';
|
||||
import { IconLayer, TextLayer } from '@deck.gl/layers';
|
||||
import { DougalBinaryBundle, DougalBinaryChunkSequential, DougalBinaryChunkInterleaved } from '@dougal/binary';
|
||||
|
||||
let deck;
|
||||
@@ -620,7 +622,8 @@ export default {
|
||||
|
||||
heatmapValue: "total_error",
|
||||
isFullscreen: false,
|
||||
crosshairsPosition: [],
|
||||
crosshairsPositions: [],
|
||||
crosshairsLabels: [],
|
||||
|
||||
searchText: "",
|
||||
searchVisible: false,
|
||||
@@ -1296,19 +1299,31 @@ export default {
|
||||
}
|
||||
|
||||
if (hash) {
|
||||
const [ view, layers, crosshairs ] = hash.split(":");
|
||||
const [ view, layers, crosshairs, labels ] = hash.split(":");
|
||||
|
||||
if (view) {
|
||||
const key = {x: "longitude", y: "latitude", z: "zoom", p: "pitch", b: "bearing"};
|
||||
const entries = view
|
||||
.split(/([xyzpb])/)
|
||||
.filter( v => v.length )
|
||||
.reduce( (acc, cur, idx) =>
|
||||
(idx % 2 ? acc[acc.length-1].push(cur) : acc.push([cur]), acc), []
|
||||
).map( ([k, v]) => [ key[k], Number(v) ] )
|
||||
.filter( ([k, v]) => k && !isNaN(v) );
|
||||
if (crosshairs) {
|
||||
this.crosshairsPositions = crosshairs
|
||||
.split(";")
|
||||
.map(p => p
|
||||
.split(",", 2)
|
||||
.filter( v => v.trim().length )
|
||||
.map( v => Number(v) )
|
||||
.filter( v => !isNaN(v) )
|
||||
)
|
||||
.filter( v => v.length == 2);
|
||||
|
||||
this.viewState = Object.fromEntries(entries);
|
||||
if (this.crosshairsPositions.length) {
|
||||
if (!this.layerSelection.includes("crosshairs")) {
|
||||
this.layerSelection.push("crosshairs");
|
||||
}
|
||||
}
|
||||
|
||||
if (labels) {
|
||||
this.crosshairsLabels = labels.split("\u001F").map( i => decodeURIComponent(i) );
|
||||
if (!this.layerSelection.includes("labels")) {
|
||||
this.layerSelection.push("labels");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1343,15 +1358,18 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (crosshairs) {
|
||||
const [ x, y ] = crosshairs.split(",").map(i => Number(i));
|
||||
if (view) {
|
||||
const key = {x: "longitude", y: "latitude", z: "zoom", p: "pitch", b: "bearing"};
|
||||
const entries = view
|
||||
.split(/([xyzpb])/)
|
||||
.filter( v => v.length )
|
||||
.reduce( (acc, cur, idx) =>
|
||||
(idx % 2 ? acc[acc.length-1].push(cur) : acc.push([cur]), acc), []
|
||||
).map( ([k, v]) => [ key[k], Number(v) ] )
|
||||
.filter( ([k, v]) => k && !isNaN(v) );
|
||||
|
||||
this.viewState = Object.fromEntries(entries);
|
||||
|
||||
if (!isNaN(x) && !isNaN(y)) {
|
||||
this.crosshairsPosition = [ x, y ];
|
||||
if (!this.layerSelection.includes("crosshairs")) {
|
||||
this.layerSelection.push("crosshairs");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1484,12 +1502,12 @@ export default {
|
||||
this.layersAvailable.seqfp = this.finalSequencesPointsLayer;
|
||||
|
||||
this.layersAvailable.seqrh = this.heatmapLayer;
|
||||
|
||||
this.layersAvailable.crosshairs = (options = {}) => {
|
||||
return new IconLayer({
|
||||
id: 'crosshairs',
|
||||
data: [{position: [...this.crosshairsPosition]}],
|
||||
getSize: 24,
|
||||
data: this.crosshairsPositions,
|
||||
getPosition: d => d,
|
||||
getSize: 32,
|
||||
getColor: [ 255, 0, 0 ],
|
||||
getIcon: (d) => {
|
||||
return {
|
||||
@@ -1502,6 +1520,21 @@ export default {
|
||||
});
|
||||
};
|
||||
|
||||
this.layersAvailable.labels = (options = {}) => {
|
||||
return new TextLayer({
|
||||
id: 'labels',
|
||||
data: this.crosshairsPositions,
|
||||
getPosition: d => d,
|
||||
getSize: 18,
|
||||
getColor: [ 255, 0, 0 ],
|
||||
getText: (d, {index}) => this.crosshairsLabels[index],
|
||||
getPixelOffset: [ 0, -24 ],
|
||||
getTextAnchor: "start",
|
||||
getAlignmentBase: "bottom",
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
deck = new Deck({
|
||||
parent: document.getElementById("map"),
|
||||
mapStyle: 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
|
||||
|
||||
Reference in New Issue
Block a user