Show events on map.

Partial implementation. Notably, it does not yet update
when events are added/modified/deleted.

Related to #48.
This commit is contained in:
D. Berge
2020-09-25 18:33:55 +02:00
parent 42d453f714
commit 949f42c1dc
3 changed files with 99 additions and 5 deletions

View File

@@ -16,12 +16,18 @@
.theme--dark #map {
background-color: #111;
}
.cluster {
border: 2px solid green;
border-radius: 6px;
}
</style>
<script>
import L from 'leaflet'
import 'leaflet-realtime'
import { mapActions, mapGetters } from 'vuex';
import 'leaflet.markercluster'
import { mapActions, mapGetters, mapState } from 'vuex';
import ftstamp from '@/lib/FormatTimestamp'
import zoomFitIcon from '@/assets/zoom-fit-best.svg'
@@ -135,6 +141,10 @@ const layers = {
layer.bindTooltip(popup, {sticky: true});
}
}),
"Events (QC)": L.geoJSON(null),
"Events (Other)": L.geoJSON(null),
"Real-time": L.realtime({
url: '/api/navdata/gis/point?limit=1',
@@ -235,6 +245,7 @@ function makeRealTimePopup(feature) {
return popup;
}
export default {
name: "Map",
@@ -272,7 +283,8 @@ export default {
},
computed: {
...mapGetters(['loading', 'serverEvent', 'lineName'])
...mapGetters(['loading', 'serverEvent', 'lineName', 'serverEvent']),
...mapState({projectSchema: state => state.project.projectSchema})
},
watch: {
@@ -298,6 +310,8 @@ export default {
};
rtLayer.update(geojson);
}
} else if (event.channel == "event" && event.payload.schema == this.projectSchema) {
console.log("EVENT", event);
}
}
},
@@ -309,6 +323,44 @@ export default {
const bbox = new L.GeoJSON(res);
map.fitBounds(bbox.getBounds());
},
getEvents (ffn = i => true) {
return async (success, error) => {
const url = `/project/${this.$route.params.project}/event`;
const data = await this.api([url]);
if (data) {
function colour(feature) {
if (feature && feature.properties && feature.properties.type) {
if (feature.properties.type == "qc") {
return feature.properties.labels.includes("QCAccepted")
? "lightgray"
: "gray";
} else if (feature.properties.type == "midnight shot") {
return "cyan";
} else {
return "orange";
}
}
return "brown";
}
const features = data.filter(i => i.geometry).filter(ffn).map(i => {
const feature = {
type: "Feature",
geometry: i.geometry,
properties: i
};
feature.properties.colour = colour(feature);
delete feature.properties.geometry;
return feature;
});
success(features);
} else {
error("Failed to get events");
}
}
},
async refreshLayers (layerset) {
@@ -329,9 +381,17 @@ export default {
// Firing all refresh events asynchronously, which is OK provided
// we don't have hundreds of layers to be refreshed.
await this.api([l.url(query)]).then( (layer) => {
if (typeof l.transform == 'function') {
layer = l.transform(layer);
}
l.layer.clearLayers();
if ((layer.features && layer.features.length < limit) || ("length" in layer && layer.length < limit)) {
l.layer.addData(layer);
if (layer instanceof L.Layer || (layer.features && layer.features.length < limit) || ("length" in layer && layer.length < limit)) {
if (l.layer.addData) {
l.layer.addData(layer);
} else if (l.layer.addLayer) {
l.layer.addLayer(layer);
}
} else {
console.warn("Too much data from", l.url(query));
}
@@ -380,6 +440,35 @@ export default {
mounted () {
map = L.map('map', {maxZoom: 22});
const eventsOptions = () => {
return {
//start: false,
container: L.markerClusterGroup({maxClusterRadius: 1, className: "cluster"}),
getFeatureId (feature) {
return feature.properties.id;
},
pointToLayer (point, latlng) {
return L.circleMarker(latlng, {
radius: 5,
color: point.properties.colour || "gray",
stroke: false,
fillOpacity: 0.6
});
},
onEachFeature (feature, layer) {
const p = feature.properties;
const popup = (p.sequence
? `Event @ ${p.tstamp}<br/>Sequence ${p.sequence}<br/>Point <b>${p.line} / ${p.point}</b><br/><hr/>${p.remarks}`
: `Event @ ${p.tstamp}<br/><hr/>${p.remarks}`)
+ (p.labels.length? `<br/>[<i>${p.labels.join(", ")}</i>]` : "");
layer.bindTooltip(popup, {sticky: true});
}
}
};
layers["Events (QC)"] = L.realtime(this.getEvents(i => i.type == "qc"), eventsOptions());
layers["Events (Other)"] = L.realtime(this.getEvents(i => i.type != "qc"), eventsOptions());
const init = this.decodeURL();
@@ -436,7 +525,6 @@ export default {
this.fitProjectBounds();
}
// /usr/share/icons/breeze/actions/16/zoom-fit-best.svg
const fitProjectBounds = this.fitProjectBounds;
const FitToBoundsControl = L.Control.extend({