mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 10:47: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 lines available --></label>
|
||||||
<label><!-- No heatmap 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>
|
</div>
|
||||||
<!--
|
<!--
|
||||||
<input id="lyr-psl" type="checkbox" value="psl" v-model="layerSelection"/>
|
<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"/>
|
<input id="lyr-nav" type="checkbox" value="nav" v-model="layerSelection"/>
|
||||||
<label for="lyr-nav" title="Vessel track">Navigation trail</label>
|
<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,
|
<!-- 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
|
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 zoomFitIcon from '@/assets/zoom-fit-best.svg'
|
||||||
|
|
||||||
import { Deck, FlyToInterpolator } from '@deck.gl/core';
|
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';
|
import { DougalBinaryBundle, DougalBinaryChunkSequential, DougalBinaryChunkInterleaved } from '@dougal/binary';
|
||||||
|
|
||||||
let deck;
|
let deck;
|
||||||
@@ -620,7 +622,8 @@ export default {
|
|||||||
|
|
||||||
heatmapValue: "total_error",
|
heatmapValue: "total_error",
|
||||||
isFullscreen: false,
|
isFullscreen: false,
|
||||||
crosshairsPosition: [],
|
crosshairsPositions: [],
|
||||||
|
crosshairsLabels: [],
|
||||||
|
|
||||||
searchText: "",
|
searchText: "",
|
||||||
searchVisible: false,
|
searchVisible: false,
|
||||||
@@ -1296,19 +1299,31 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hash) {
|
if (hash) {
|
||||||
const [ view, layers, crosshairs ] = hash.split(":");
|
const [ view, layers, crosshairs, labels ] = hash.split(":");
|
||||||
|
|
||||||
if (view) {
|
if (crosshairs) {
|
||||||
const key = {x: "longitude", y: "latitude", z: "zoom", p: "pitch", b: "bearing"};
|
this.crosshairsPositions = crosshairs
|
||||||
const entries = view
|
.split(";")
|
||||||
.split(/([xyzpb])/)
|
.map(p => p
|
||||||
.filter( v => v.length )
|
.split(",", 2)
|
||||||
.reduce( (acc, cur, idx) =>
|
.filter( v => v.trim().length )
|
||||||
(idx % 2 ? acc[acc.length-1].push(cur) : acc.push([cur]), acc), []
|
.map( v => Number(v) )
|
||||||
).map( ([k, v]) => [ key[k], Number(v) ] )
|
.filter( v => !isNaN(v) )
|
||||||
.filter( ([k, v]) => k && !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) {
|
if (view) {
|
||||||
const [ x, y ] = crosshairs.split(",").map(i => Number(i));
|
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.seqfp = this.finalSequencesPointsLayer;
|
||||||
|
|
||||||
this.layersAvailable.seqrh = this.heatmapLayer;
|
this.layersAvailable.seqrh = this.heatmapLayer;
|
||||||
|
|
||||||
this.layersAvailable.crosshairs = (options = {}) => {
|
this.layersAvailable.crosshairs = (options = {}) => {
|
||||||
return new IconLayer({
|
return new IconLayer({
|
||||||
id: 'crosshairs',
|
id: 'crosshairs',
|
||||||
data: [{position: [...this.crosshairsPosition]}],
|
data: this.crosshairsPositions,
|
||||||
getSize: 24,
|
getPosition: d => d,
|
||||||
|
getSize: 32,
|
||||||
getColor: [ 255, 0, 0 ],
|
getColor: [ 255, 0, 0 ],
|
||||||
getIcon: (d) => {
|
getIcon: (d) => {
|
||||||
return {
|
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({
|
deck = new Deck({
|
||||||
parent: document.getElementById("map"),
|
parent: document.getElementById("map"),
|
||||||
mapStyle: 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
|
mapStyle: 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
|
||||||
|
|||||||
Reference in New Issue
Block a user