Merge branch 'frontend/timo_neu' into develop
This commit is contained in:
commit
698e0322b3
@ -19,7 +19,9 @@
|
||||
"openlayers": "^4.6.5",
|
||||
"quasar": "^1.0.0-rc.2",
|
||||
"vue-qrcode-reader": "^1.4.2",
|
||||
"vuelayers": "^0.11.4"
|
||||
"vuelayers": "^0.11.4",
|
||||
"lodash": "^4.17.11",
|
||||
"ol": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/app": "^1.0.0-rc.4",
|
||||
|
||||
@ -0,0 +1,408 @@
|
||||
<template>
|
||||
<div style="height: 400px">
|
||||
<vl-map v-if="mapVisible" class="map" ref="map" :load-tiles-while-animating="true"
|
||||
:load-tiles-while-interacting="true"
|
||||
@click="clickCoordinate = $event.coordinate" @postcompose="onMapPostCompose"
|
||||
data-projection="EPSG:4326" @mounted="onMapMounted">
|
||||
<!-- map view aka ol.View -->
|
||||
<vl-view ref="view" :center.sync="computedCoords" :zoom.sync="zoom" :rotation.sync="rotation"></vl-view>
|
||||
|
||||
<!-- interactions -->
|
||||
<vl-interaction-select :features.sync="selectedFeatures" v-if="drawType == null">
|
||||
<template slot-scope="select">
|
||||
<!-- select styles -->
|
||||
<vl-style-box>
|
||||
<vl-style-stroke color="#423e9e" :width="7"></vl-style-stroke>
|
||||
<vl-style-fill :color="[254, 178, 76, 0.7]"></vl-style-fill>
|
||||
<vl-style-circle :radius="5">
|
||||
<vl-style-stroke color="#423e9e" :width="7"></vl-style-stroke>
|
||||
<vl-style-fill :color="[254, 178, 76, 0.7]"></vl-style-fill>
|
||||
</vl-style-circle>
|
||||
</vl-style-box>
|
||||
<vl-style-box :z-index="1">
|
||||
<vl-style-stroke color="#d43f45" :width="2"></vl-style-stroke>
|
||||
<vl-style-circle :radius="5">
|
||||
<vl-style-stroke color="#d43f45" :width="2"></vl-style-stroke>
|
||||
</vl-style-circle>
|
||||
</vl-style-box>
|
||||
<!--// select styles -->
|
||||
|
||||
<!-- selected feature popup -->
|
||||
<vl-overlay class="feature-popup" v-for="feature in select.features" :key="feature.id" :id="feature.id"
|
||||
:position="pointOnSurface(feature.geometry)" :auto-pan="true"
|
||||
:auto-pan-animation="{ duration: 300 }">
|
||||
<template slot-scope="popup">
|
||||
<section class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Feature ID {{ feature.id }}
|
||||
</p>
|
||||
<a class="card-header-icon" title="Close"
|
||||
@click="selectedFeatures = selectedFeatures.filter(f => f.id !== feature.id)">
|
||||
<b-icon icon="close"></b-icon>
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<p>
|
||||
Overlay popup content for Feature with ID <strong>{{ feature.id }}</strong>
|
||||
</p>
|
||||
<p>
|
||||
Popup: {{ JSON.stringify(popup) }}
|
||||
</p>
|
||||
<p>
|
||||
Feature: {{ JSON.stringify({ id: feature.id, properties: feature.properties }) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</vl-overlay>
|
||||
<!--// selected popup -->
|
||||
</template>
|
||||
</vl-interaction-select>
|
||||
<!--// interactions -->
|
||||
|
||||
<!-- geolocation -->
|
||||
<vl-geoloc @update:position="onUpdatePosition">
|
||||
<template slot-scope="geoloc">
|
||||
<!-- !!Snippet -->
|
||||
<vl-feature v-if="geoloc.position" id="position-feature">
|
||||
<vl-geom-point :coordinates="geoloc.position"></vl-geom-point>
|
||||
<vl-style-box>
|
||||
<vl-style-icon src="./statics/map-marker.svg" :scale="2" :anchor="[0.095,0.17]"
|
||||
:size="[64, 64]"></vl-style-icon>
|
||||
</vl-style-box>
|
||||
</vl-feature>
|
||||
<!-- !!Snippet -->
|
||||
</template>
|
||||
</vl-geoloc>
|
||||
<!--// geolocation -->
|
||||
|
||||
<!-- <vl-feature id="point">-->
|
||||
<!-- <vl-geom-point :coordinates=""></vl-geom-point>-->
|
||||
<!-- </vl-feature>-->
|
||||
<!-- overlay marker with animation -->
|
||||
<!-- !!Snippet -->
|
||||
<!-- <vl-feature id="marker" ref="marker" :properties="{ start: Date.now(), duration: 2500 }">-->
|
||||
<vl-feature id="marker" ref="marker">
|
||||
<!-- <template slot-scope="feature">-->
|
||||
<template>
|
||||
<vl-geom-point :coordinates="center"></vl-geom-point>
|
||||
<vl-style-box>
|
||||
<vl-style-icon src="./statics/map-marker.svg" :scale="2" :anchor="[0.095,0.17]"
|
||||
:size="[128, 128]"></vl-style-icon>
|
||||
</vl-style-box>
|
||||
<!-- overlay binded to feature -->
|
||||
<!-- <vl-overlay v-if="feature.geometry" :position="pointOnSurface(feature.geometry)" :offset="[10, 10]">-->
|
||||
<!-- <p class="is-light box content">-->
|
||||
<!-- Always opened overlay for Feature ID <strong>{{ feature.id }}</strong>-->
|
||||
<!-- </p>-->
|
||||
<!-- </vl-overlay>-->
|
||||
</template>
|
||||
</vl-feature>
|
||||
<!-- !!Snippet -->
|
||||
<!--// overlay marker -->
|
||||
|
||||
|
||||
<!-- base layers -->
|
||||
<vl-layer-tile v-for="layer in baseLayers" :key="layer.name" :id="layer.name" :visible="layer.visible">
|
||||
<component :is="'vl-source-' + layer.name" v-bind="layer"></component>
|
||||
</vl-layer-tile>
|
||||
<!--// base layers -->
|
||||
|
||||
<!-- draw components -->
|
||||
<!-- <vl-layer-vector id="draw-pane" v-if="mapPanel.tab === 'state'">-->
|
||||
<!-- <vl-source-vector ident="draw-target" :features.sync="drawnFeatures">-->
|
||||
|
||||
<!-- </vl-source-vector>-->
|
||||
<!-- </vl-layer-vector>-->
|
||||
|
||||
<!-- <vl-interaction-draw v-if="mapPanel.tab === 'state' && drawType" source="draw-target" :type="drawType"></vl-interaction-draw>-->
|
||||
<!-- <vl-interaction-modify v-if="mapPanel.tab === 'state'" source="draw-target"></vl-interaction-modify>-->
|
||||
<!-- <vl-interaction-snap v-if="mapPanel.tab === 'state'" source="draw-target" :priority="10"></vl-interaction-snap>-->
|
||||
|
||||
</vl-map>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import VueLayers from 'vuelayers'
|
||||
import 'vuelayers/lib/style.css' // needs css-loader
|
||||
import {camelCase, kebabCase} from 'lodash'
|
||||
import {addProj, createMultiPointGeom, createProj, createStyle, findPointOnSurface} from 'vuelayers/lib/ol-ext'
|
||||
import ScaleLine from 'ol/control/ScaleLine'
|
||||
import FullScreen from 'ol/control/FullScreen'
|
||||
import OverviewMap from 'ol/control/OverviewMap'
|
||||
import ZoomSlider from 'ol/control/ZoomSlider'
|
||||
|
||||
Vue.use(VueLayers);
|
||||
|
||||
// Custom projection for static Image layer
|
||||
let x = 1024 * 10000
|
||||
let y = 968 * 10000
|
||||
let imageExtent = [-x / 2, -y / 2, x / 2, y / 2]
|
||||
let customProj = createProj({
|
||||
code: 'xkcd-image',
|
||||
units: 'pixels',
|
||||
extent: imageExtent,
|
||||
})
|
||||
// add to the list of known projections
|
||||
// after that it can be used by code
|
||||
addProj(customProj)
|
||||
|
||||
const easeInOut = t => 1 - Math.pow(1 - t, 3)
|
||||
|
||||
const methods = {
|
||||
camelCase,
|
||||
pointOnSurface: findPointOnSurface,
|
||||
geometryTypeToCmpName(type) {
|
||||
return 'vl-geom-' + kebabCase(type)
|
||||
},
|
||||
/**
|
||||
* Packman layer Style function factory
|
||||
* @return {ol.StyleFunction}
|
||||
*/
|
||||
pacmanStyleFunc() {
|
||||
const pacman = [
|
||||
createStyle({
|
||||
strokeColor: '#de9147',
|
||||
strokeWidth: 3,
|
||||
fillColor: [222, 189, 36, 0.8],
|
||||
}),
|
||||
]
|
||||
const path = [
|
||||
createStyle({
|
||||
strokeColor: 'blue',
|
||||
strokeWidth: 1,
|
||||
}),
|
||||
createStyle({
|
||||
imageRadius: 5,
|
||||
imageFillColor: 'orange',
|
||||
geom(feature) {
|
||||
// geometry is an LineString, convert it to MultiPoint to style vertex
|
||||
return createMultiPointGeom(feature.getGeometry().getCoordinates())
|
||||
},
|
||||
}),
|
||||
]
|
||||
const eye = [
|
||||
createStyle({
|
||||
imageRadius: 6,
|
||||
imageFillColor: '#444444',
|
||||
}),
|
||||
]
|
||||
|
||||
return function __pacmanStyleFunc(feature) {
|
||||
switch (feature.getId()) {
|
||||
case 'pacman':
|
||||
return pacman
|
||||
case 'pacman-path':
|
||||
return path
|
||||
case 'pacman-eye':
|
||||
return eye
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Cluster layer style function factory
|
||||
* @return {ol.StyleFunction}
|
||||
*/
|
||||
clusterStyleFunc() {
|
||||
const cache = {}
|
||||
|
||||
return function __clusterStyleFunc(feature) {
|
||||
const size = feature.get('features').length
|
||||
let style = cache[size]
|
||||
|
||||
if (!style) {
|
||||
style = createStyle({
|
||||
imageRadius: 10,
|
||||
strokeColor: '#fff',
|
||||
fillColor: '#3399cc',
|
||||
text: size.toString(),
|
||||
textFillColor: '#fff',
|
||||
})
|
||||
cache[size] = style
|
||||
}
|
||||
return [style]
|
||||
}
|
||||
},
|
||||
selectFilter(feature) {
|
||||
return ['position-feature'].indexOf(feature.getId()) === -1
|
||||
},
|
||||
onUpdatePosition(coordinate) {
|
||||
console.log("onUpdatePosition")
|
||||
this.deviceCoordinate = coordinate;
|
||||
this.$emit('updatecoords', this.center);
|
||||
},
|
||||
onMapPostCompose({vectorContext, frameState}) {
|
||||
if (!this.$refs.marker || !this.$refs.marker.$feature) return
|
||||
|
||||
const feature = this.$refs.marker.$feature
|
||||
if (!feature.getGeometry() || !feature.getStyle()) return
|
||||
|
||||
const flashGeom = feature.getGeometry().clone()
|
||||
const duration = feature.get('duration')
|
||||
const elapsed = frameState.time - feature.get('start')
|
||||
const elapsedRatio = elapsed / duration
|
||||
const radius = easeInOut(elapsedRatio) * 35 + 5
|
||||
const opacity = easeInOut(1 - elapsedRatio)
|
||||
const fillOpacity = easeInOut(0.5 - elapsedRatio)
|
||||
|
||||
vectorContext.setStyle(createStyle({
|
||||
imageRadius: radius,
|
||||
fillColor: [119, 170, 203, fillOpacity],
|
||||
strokeColor: [119, 170, 203, opacity],
|
||||
strokeWidth: 2 + opacity,
|
||||
}))
|
||||
|
||||
vectorContext.drawGeometry(flashGeom)
|
||||
vectorContext.setStyle(feature.getStyle()(feature)[0])
|
||||
vectorContext.drawGeometry(feature.getGeometry())
|
||||
|
||||
if (elapsed > duration) {
|
||||
feature.set('start', Date.now())
|
||||
}
|
||||
|
||||
this.$refs.map.render()
|
||||
},
|
||||
onMapMounted() {
|
||||
// now ol.Map instance is ready and we can work with it directly
|
||||
this.$refs.map.$map.getControls().extend([
|
||||
new ScaleLine(),
|
||||
new FullScreen(),
|
||||
new OverviewMap({
|
||||
collapsed: false,
|
||||
collapsible: true,
|
||||
}),
|
||||
new ZoomSlider(),
|
||||
])
|
||||
},
|
||||
// base layers
|
||||
showBaseLayer(name) {
|
||||
let layer = this.baseLayers.find(layer => layer.visible)
|
||||
if (layer != null) {
|
||||
layer.visible = false
|
||||
}
|
||||
|
||||
layer = this.baseLayers.find(layer => layer.name === name)
|
||||
if (layer != null) {
|
||||
layer.visible = true
|
||||
}
|
||||
},
|
||||
// map panel
|
||||
mapPanelTabClasses(tab) {
|
||||
return {
|
||||
'is-active': this.mapPanel.tab === tab,
|
||||
}
|
||||
},
|
||||
showMapPanelLayer(layer) {
|
||||
layer.visible = !layer.visible
|
||||
},
|
||||
showMapPanelTab(tab) {
|
||||
this.mapPanel.tab = tab
|
||||
if (tab !== 'draw') {
|
||||
this.drawType = undefined
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
name: 'mapclickable',
|
||||
methods,
|
||||
props: {
|
||||
coords: Array,
|
||||
cacheObject: Object,
|
||||
stationObject: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
center: [9.208858198755664, 49.14785422283188],
|
||||
zoom: 15,
|
||||
rotation: 0,
|
||||
clickCoordinate: undefined,
|
||||
selectedFeatures: [],
|
||||
deviceCoordinate: undefined,
|
||||
mapPanel: {
|
||||
tab: 'state',
|
||||
},
|
||||
panelOpen: true,
|
||||
mapVisible: true,
|
||||
drawControls: [
|
||||
{
|
||||
type: 'point',
|
||||
label: 'Draw Point',
|
||||
icon: 'map-marker',
|
||||
},
|
||||
{
|
||||
type: 'line-string',
|
||||
label: 'Draw LineString',
|
||||
icon: 'minus',
|
||||
},
|
||||
{
|
||||
type: 'polygon',
|
||||
label: 'Draw Polygon',
|
||||
icon: 'square-o',
|
||||
},
|
||||
{
|
||||
type: 'circle',
|
||||
label: 'Draw Circle',
|
||||
icon: 'circle-thin',
|
||||
},
|
||||
{
|
||||
type: undefined,
|
||||
label: 'Stop drawing',
|
||||
icon: 'times',
|
||||
},
|
||||
],
|
||||
drawType: "point",
|
||||
drawnFeatures: [],
|
||||
// base layers
|
||||
baseLayers: [
|
||||
{
|
||||
name: 'osm',
|
||||
title: 'OpenStreetMap',
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
name: 'sputnik',
|
||||
title: 'Sputnik Maps',
|
||||
visible: false,
|
||||
},
|
||||
// needs paid plan to get key
|
||||
// {
|
||||
// name: 'mapbox',
|
||||
// title: 'Mapbox',
|
||||
// },
|
||||
{
|
||||
name: 'bingmaps',
|
||||
title: 'Bing Maps',
|
||||
apiKey: 'ArbsA9NX-AZmebC6VyXAnDqjXk6mo2wGCmeYM8EwyDaxKfQhUYyk0jtx6hX5fpMn',
|
||||
imagerySet: 'CanvasGray',
|
||||
visible: false,
|
||||
},
|
||||
],
|
||||
// layers config
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
computed: {
|
||||
computedCoords: {
|
||||
get() {
|
||||
return this.center;
|
||||
},
|
||||
set(val) {
|
||||
this.center = val;
|
||||
this.$emit('updatecoords', this.center);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@ -251,8 +251,8 @@
|
||||
}
|
||||
},
|
||||
validateRankingPoints: function () {
|
||||
var re = new RegExp('^[0-9]+$');
|
||||
var rps = String.valueOf(this.computedCache.rankingPoints);
|
||||
let re = new RegExp('^[0-9]+$');
|
||||
let rps = String(this.computedCache.rankingPoints);
|
||||
return re.test(rps);
|
||||
},
|
||||
validateEverything(){
|
||||
|
||||
@ -15,25 +15,32 @@
|
||||
<!--type="textarea"-->
|
||||
<!--/>-->
|
||||
<p class="text-h6 q-mt-md">Location</p>
|
||||
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true"
|
||||
data-projection="EPSG:4326">
|
||||
<vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>
|
||||
<mapclick :coords="[this.station.longitude, this.station.lattitude]" @updatecoords="updateCoords($event)">
|
||||
|
||||
<vl-geoloc @update:position="geolocPosition = $event">
|
||||
<template slot-scope="geoloc">
|
||||
<vl-feature v-if="geoloc.position" id="position-feature">
|
||||
<vl-geom-point :coordinates="geoloc.position"></vl-geom-point>
|
||||
</vl-feature>
|
||||
</template>
|
||||
</vl-geoloc>
|
||||
</mapclick>
|
||||
<!-- <vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true"-->
|
||||
<!-- data-projection="EPSG:4326">-->
|
||||
<!-- <vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>-->
|
||||
|
||||
<vl-layer-tile id="osm">
|
||||
<vl-source-osm></vl-source-osm>
|
||||
</vl-layer-tile>
|
||||
</vl-map>
|
||||
<!-- <vl-geoloc @update:position="geolocPosition = $event">-->
|
||||
<!-- <template slot-scope="geoloc">-->
|
||||
<!-- <vl-feature v-if="geoloc.position" id="position-feature">-->
|
||||
<!-- <vl-geom-point :coordinates="geoloc.position"></vl-geom-point>-->
|
||||
<!-- </vl-feature>-->
|
||||
<!-- </template>-->
|
||||
<!-- </vl-geoloc>-->
|
||||
|
||||
<!-- <vl-layer-tile id="osm">-->
|
||||
<!-- <vl-source-osm></vl-source-osm>-->
|
||||
<!-- </vl-layer-tile>-->
|
||||
<!-- </vl-map>-->
|
||||
<div class="row q-col-gutter-md">
|
||||
<q-input class="col" dense stack-label filled v-model="latlang" @input="separateLatlang"
|
||||
label="Breitengrad/Längengrad"/>
|
||||
<q-input class="col-12" dense stack-label filled v-model="station.lattitude"
|
||||
label="Breitengrad"/>
|
||||
<q-input class="col-12" dense stack-label filled v-model="station.longitude"
|
||||
label="Längengrad"/>
|
||||
<div class="col-shrink">
|
||||
<q-btn unelevated color="primary" class="full-height" icon="my_location"/>
|
||||
</div>
|
||||
@ -51,10 +58,11 @@
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex';
|
||||
import mapclick from "../components/mapClickable";
|
||||
|
||||
export default {
|
||||
name: "Station",
|
||||
|
||||
components: { mapclick },
|
||||
data() {
|
||||
return {
|
||||
description: "Rätsel, Aufgabe und Informationen zur Station.",
|
||||
@ -91,8 +99,12 @@
|
||||
...mapGetters({
|
||||
tempStation: 'cacheCollector/GET_TEMPSTATION'
|
||||
}),
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateCoords(event) {
|
||||
this.station.longitude = event[0];
|
||||
this.station.lattitude = event[1];
|
||||
},
|
||||
separateLatlang() {
|
||||
//console.log("separateLatlang()");
|
||||
if (this.latlang.includes(',')) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user