Marker layer alternative
This commit is contained in:
@@ -288,7 +288,11 @@ class Spot extends Main
|
|||||||
$iIndex = 0;
|
$iIndex = 0;
|
||||||
$iMaxIndex = count($asMessages) - 1;
|
$iMaxIndex = count($asMessages) - 1;
|
||||||
foreach($asMedias as $asMedia) {
|
foreach($asMedias as $asMedia) {
|
||||||
if($asMedia['latitude']!='' && $asMedia['longitude']!='') $asGeoMedias[] = $asMedia;
|
if($asMedia['latitude']!='' && $asMedia['longitude']!='') {
|
||||||
|
$asMedia['lat_dms'] = self::decToDms($asMedia['latitude'], 'lat');
|
||||||
|
$asMedia['lon_dms'] = self::decToDms($asMedia['longitude'], 'lon');
|
||||||
|
$asGeoMedias[] = $asMedia;
|
||||||
|
}
|
||||||
elseif($bHasMsg) {
|
elseif($bHasMsg) {
|
||||||
while($iIndex <= $iMaxIndex && $asMedia['unix_time'] > $asMessages[$iIndex]['unix_time']) $iIndex++;
|
while($iIndex <= $iMaxIndex && $asMedia['unix_time'] > $asMessages[$iIndex]['unix_time']) $iIndex++;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import waitforimages from 'jquery.waitforimages';
|
|||||||
import lightbox from '../scripts/lightbox.js';
|
import lightbox from '../scripts/lightbox.js';
|
||||||
|
|
||||||
import SpotIcon from './spotIcon.vue';
|
import SpotIcon from './spotIcon.vue';
|
||||||
|
import SpotIconStack from './spotIconStack.vue';
|
||||||
import SpotButton from './spotButton.vue';
|
import SpotButton from './spotButton.vue';
|
||||||
import ProjectPost from './projectPost.vue';
|
import ProjectPost from './projectPost.vue';
|
||||||
import ProjectPopup from './projectPopup.vue';
|
import ProjectPopup from './projectPopup.vue';
|
||||||
@@ -19,7 +20,6 @@ export default {
|
|||||||
SpotIcon,
|
SpotIcon,
|
||||||
SpotButton,
|
SpotButton,
|
||||||
ProjectPost,
|
ProjectPost,
|
||||||
ProjectPopup,
|
|
||||||
Simplebar
|
Simplebar
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -30,6 +30,7 @@ export default {
|
|||||||
lastUpdate: { unix_time: 0, relative_time: '', formatted_time: ''},
|
lastUpdate: { unix_time: 0, relative_time: '', formatted_time: ''},
|
||||||
feedPanelOpen: false,
|
feedPanelOpen: false,
|
||||||
settingsPanelOpen: false,
|
settingsPanelOpen: false,
|
||||||
|
markers: {messages: null, medias: null},
|
||||||
markerSize: {width: 32, height: 32},
|
markerSize: {width: 32, height: 32},
|
||||||
currProject: {},
|
currProject: {},
|
||||||
modeHisto: false,
|
modeHisto: false,
|
||||||
@@ -38,7 +39,6 @@ export default {
|
|||||||
nlLoading: false,
|
nlLoading: false,
|
||||||
baseMaps: {},
|
baseMaps: {},
|
||||||
baseMap: null,
|
baseMap: null,
|
||||||
messages: null,
|
|
||||||
map: null,
|
map: null,
|
||||||
hikes: {
|
hikes: {
|
||||||
colors:{'main':'#00ff78', 'off-track':'#0000ff', 'hitchhiking':'#FF7814'},
|
colors:{'main':'#00ff78', 'off-track':'#0000ff', 'hitchhiking':'#FF7814'},
|
||||||
@@ -190,7 +190,8 @@ export default {
|
|||||||
const aoMarkers = await this.spot.get2('markers', {id_project: this.currProject.id});
|
const aoMarkers = await this.spot.get2('markers', {id_project: this.currProject.id});
|
||||||
this.baseMap = null;
|
this.baseMap = null;
|
||||||
this.baseMaps = aoMarkers.maps;
|
this.baseMaps = aoMarkers.maps;
|
||||||
this.messages = aoMarkers.messages;
|
this.markers.messages = aoMarkers.messages;
|
||||||
|
this.markers.medias = aoMarkers.medias;
|
||||||
this.lastUpdate = aoMarkers.last_update;
|
this.lastUpdate = aoMarkers.last_update;
|
||||||
|
|
||||||
//console.log(this.baseMaps);
|
//console.log(this.baseMaps);
|
||||||
@@ -271,7 +272,7 @@ export default {
|
|||||||
type:'geojson',
|
type:'geojson',
|
||||||
data: {
|
data: {
|
||||||
type: 'FeatureCollection',
|
type: 'FeatureCollection',
|
||||||
features: this.convertMsgToFeatures(this.messages)
|
features: this.convertMsgToFeatures(this.markers.messages)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.map.addLayer({
|
this.map.addLayer({
|
||||||
@@ -284,16 +285,60 @@ export default {
|
|||||||
this.openMarkerPopup(e.features[0]);
|
this.openMarkerPopup(e.features[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
this.markers.messages.forEach(msg => {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
|
||||||
|
const app = createApp(SpotIconStack, {iconMain: 'message', iconSub:'message-in', iconSubClasses:'fa-rotate-270'});
|
||||||
|
app.mount(el);
|
||||||
|
|
||||||
|
new Marker({element: el, anchor: 'bottom'})
|
||||||
|
.setLngLat([msg.longitude, msg.latitude])
|
||||||
|
.addTo(this.map);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Medias
|
||||||
|
this.markers.medias.forEach(msg => {
|
||||||
|
const $Marker = document.createElement('div');
|
||||||
|
const app = createApp(SpotIconStack, {iconMain: 'message', iconSub:'image'});
|
||||||
|
app.mount($Marker);
|
||||||
|
|
||||||
|
const $Popup = document.createElement('div');
|
||||||
|
const popupElement = new Popup({
|
||||||
|
anchor: 'bottom',
|
||||||
|
offset: [0, this.markerSize.height * -1],
|
||||||
|
closeButton: false
|
||||||
|
})
|
||||||
|
.setDOMContent($Popup)
|
||||||
|
.setLngLat([msg.longitude, msg.latitude])
|
||||||
|
.setMaxWidth(300)
|
||||||
|
.addTo(this.map)
|
||||||
|
;
|
||||||
|
console.log(msg);
|
||||||
|
const popupContent = createApp(ProjectPopup, {
|
||||||
|
options: msg,
|
||||||
|
medias: [msg],
|
||||||
|
project: this.currProject
|
||||||
|
});
|
||||||
|
popupContent.provide('spot', this.spot).mount($Popup);
|
||||||
|
|
||||||
|
new Marker({element: $Marker, anchor: 'bottom'})
|
||||||
|
.setLngLat([msg.longitude, msg.latitude])
|
||||||
|
.setPopup(popupElement)
|
||||||
|
.addTo(this.map);
|
||||||
|
});
|
||||||
|
|
||||||
//Centering map
|
//Centering map
|
||||||
let bOpenFeedPanel = !this.mobile;
|
let bOpenFeedPanel = !this.mobile;
|
||||||
let oBounds = new LngLatBounds();
|
let oBounds = new LngLatBounds();
|
||||||
if(
|
if(
|
||||||
this.currProject.mode == this.spot.consts.modes.blog &&
|
this.currProject.mode == this.spot.consts.modes.blog &&
|
||||||
this.messages.length > 0 &&
|
this.markers.messages.length > 0 &&
|
||||||
this.$parent.hash.items[2] != 'message'
|
this.$parent.hash.items[2] != 'message'
|
||||||
) {
|
) {
|
||||||
//Fit to last message
|
//Fit to last message
|
||||||
let oLastMsg = this.messages[this.messages.length - 1];
|
let oLastMsg = this.markers.messages[this.markers.messages.length - 1];
|
||||||
oBounds.extend(new LngLat(oLastMsg.longitude, oLastMsg.latitude));
|
oBounds.extend(new LngLat(oLastMsg.longitude, oLastMsg.latitude));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -343,6 +388,50 @@ export default {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
openMarkerPopup(oFeature) {
|
||||||
|
this.closeMarkerPopup();
|
||||||
|
|
||||||
|
//Convert ID Message to feature
|
||||||
|
if(typeof oFeature == 'number') {
|
||||||
|
const oMatchingFeatures = this.map.querySourceFeatures('markers', {
|
||||||
|
filter: ['==', ['get', 'id_message'], oFeature]
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!oMatchingFeatures.length) {
|
||||||
|
console.warn('Marker not found: ', oFeature);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else oFeature = oMatchingFeatures[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const $Popup = document.createElement('div');
|
||||||
|
this.popup.element = new Popup({
|
||||||
|
anchor: 'bottom',
|
||||||
|
offset: [0, this.markerSize.height * -1],
|
||||||
|
closeButton: false
|
||||||
|
})
|
||||||
|
.setDOMContent($Popup)
|
||||||
|
.setLngLat(oFeature.geometry.coordinates)
|
||||||
|
.setMaxWidth(300)
|
||||||
|
.addTo(this.map);
|
||||||
|
|
||||||
|
this.popup.content = createApp(ProjectPopup, {
|
||||||
|
options: oFeature.properties,
|
||||||
|
medias: JSON.parse(oFeature.properties.medias || '[]'),
|
||||||
|
project: this.currProject
|
||||||
|
});
|
||||||
|
this.popup.content.provide('spot', this.spot).mount($Popup);
|
||||||
|
},
|
||||||
|
closeMarkerPopup() {
|
||||||
|
if(this.popup.content) {
|
||||||
|
this.popup.content.unmount();
|
||||||
|
this.popup.content = null;
|
||||||
|
}
|
||||||
|
if(this.popup.element) {
|
||||||
|
this.popup.element.remove();
|
||||||
|
this.popup.element = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
async getNextFeed() {
|
async getNextFeed() {
|
||||||
if(!this.feed.outOfData && !this.feed.loading) {
|
if(!this.feed.outOfData && !this.feed.loading) {
|
||||||
//Get next chunk
|
//Get next chunk
|
||||||
@@ -396,69 +485,14 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO medias
|
||||||
|
|
||||||
//Message Last Update
|
//Message Last Update
|
||||||
this.lastUpdate = aoData.last_update;
|
this.lastUpdate = aoData.last_update;
|
||||||
|
|
||||||
//Reschedule
|
//Reschedule
|
||||||
this.setFeedUpdateTimer(this.refreshRate);
|
this.setFeedUpdateTimer(this.refreshRate);
|
||||||
},
|
},
|
||||||
openMarkerPopup(oFeature) {
|
|
||||||
this.closeMarkerPopup();
|
|
||||||
|
|
||||||
//Convert ID Message to feature
|
|
||||||
if(typeof oFeature == 'number') {
|
|
||||||
const oMatchingFeatures = this.map.querySourceFeatures('markers', {
|
|
||||||
filter: ['==', ['get', 'id_message'], oFeature]
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!oMatchingFeatures.length) {
|
|
||||||
console.warn('Marker not found: ', oFeature);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else oFeature = oMatchingFeatures[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const $Popup = document.createElement('div');
|
|
||||||
this.popup.element = new Popup({
|
|
||||||
anchor: 'bottom',
|
|
||||||
offset: [0, this.markerSize.height * -1],
|
|
||||||
closeButton: false
|
|
||||||
})
|
|
||||||
.setDOMContent($Popup)
|
|
||||||
.setLngLat(oFeature.geometry.coordinates)
|
|
||||||
.setMaxWidth(300)
|
|
||||||
.addTo(this.map);
|
|
||||||
|
|
||||||
const rProp = ref(oFeature.properties);
|
|
||||||
|
|
||||||
const vPopup = defineComponent({
|
|
||||||
extends: ProjectPopup,
|
|
||||||
setup: () => {
|
|
||||||
provide('spot', this.spot);
|
|
||||||
return {
|
|
||||||
options: rProp.value,
|
|
||||||
medias: JSON.parse(rProp.value.medias || '""'),
|
|
||||||
spot: this.spot,
|
|
||||||
project: this.currProject
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
this.popup.content = createApp(vPopup);
|
|
||||||
this.popup.content.mount($Popup);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
closeMarkerPopup() {
|
|
||||||
if(this.popup.content) {
|
|
||||||
this.popup.content.unmount();
|
|
||||||
this.popup.content = null;
|
|
||||||
}
|
|
||||||
if(this.popup.element) {
|
|
||||||
this.popup.element.remove();
|
|
||||||
this.popup.element = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
panToBetweenPanels(oLngLat, iZoom, fCallback) {
|
panToBetweenPanels(oLngLat, iZoom, fCallback) {
|
||||||
const iXOffset = (this.settingsPanelOpen?getOuterWidth(this.$refs.settings):0) - (this.feedPanelOpen?getOuterWidth(this.$refs.feed):0);
|
const iXOffset = (this.settingsPanelOpen?getOuterWidth(this.$refs.settings):0) - (this.feedPanelOpen?getOuterWidth(this.$refs.feed):0);
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default {
|
|||||||
<span v-if="options.comment" ref="comment" class="lb-caption-line comment desktop">
|
<span v-if="options.comment" ref="comment" class="lb-caption-line comment desktop">
|
||||||
<spotIcon :icon="'post'" :classes="'fa-lg fa-fw push'" :text-classes="'comment-text'" :text="options.comment" />
|
<spotIcon :icon="'post'" :classes="'fa-lg fa-fw push'" :text-classes="'comment-text'" :text="options.comment" />
|
||||||
</span>
|
</span>
|
||||||
<span ref="postedon" class="lb-caption-line">
|
<span ref="postedon" class="lb-caption-line">
|
||||||
<projectRelTime :icon="'upload'" :localTime="options.posted_on_formatted_time_local" :siteTime="options.posted_on_formatted_time" :offset="options.posted_on_day_offset" />
|
<projectRelTime :icon="'upload'" :localTime="options.posted_on_formatted_time_local" :siteTime="options.posted_on_formatted_time" :offset="options.posted_on_day_offset" />
|
||||||
</span>
|
</span>
|
||||||
<span ref="takenon" class="lb-caption-line">
|
<span ref="takenon" class="lb-caption-line">
|
||||||
|
|||||||
@@ -12,17 +12,12 @@ export default {
|
|||||||
projectMediaLink,
|
projectMediaLink,
|
||||||
projectRelTime
|
projectRelTime
|
||||||
},
|
},
|
||||||
//props: {
|
props: {
|
||||||
// options: Object,
|
options: Object,
|
||||||
//},
|
medias: Array,
|
||||||
data() {
|
project: Object
|
||||||
return {
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
//inject: ['options', 'medias', 'spot', 'project'],
|
inject: ['spot']
|
||||||
mounted() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import spotIcon from './spotIcon.vue';
|
import spotIcon from './spotIcon.vue';
|
||||||
|
import spotIconStack from './spotIconStack.vue';
|
||||||
import spotButton from './spotButton.vue';
|
import spotButton from './spotButton.vue';
|
||||||
import projectMediaLink from './projectMediaLink.vue';
|
import projectMediaLink from './projectMediaLink.vue';
|
||||||
import projectMapLink from './projectMapLink.vue';
|
import projectMapLink from './projectMapLink.vue';
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
spotIcon,
|
spotIcon,
|
||||||
|
spotIconStack,
|
||||||
spotButton,
|
spotButton,
|
||||||
projectMediaLink,
|
projectMediaLink,
|
||||||
projectMapLink,
|
projectMapLink,
|
||||||
@@ -159,10 +161,7 @@
|
|||||||
<span class="temperature">{{ options.weather_temp+'°C' }}</span>
|
<span class="temperature">{{ options.weather_temp+'°C' }}</span>
|
||||||
</span>
|
</span>
|
||||||
<img class="staticmap clickable" :title="spot.lang('click_zoom')" :src="options.static_img_url" />
|
<img class="staticmap clickable" :title="spot.lang('click_zoom')" :src="options.static_img_url" />
|
||||||
<span class="drill-icon fa-stack clickable">
|
<spotIconStack :iconMain="'message'" :iconSub="'message-in'" :iconSubClasses="'fa-rotate-270'" />
|
||||||
<spotIcon :icon="'message'" :classes="'fa-stack-2x clickable'" />
|
|
||||||
<spotIcon :icon="'message-in'" :classes="'fa-stack-1x fa-rotate-270'" />
|
|
||||||
</span>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="options.type == 'media'" class="body-box">
|
<div v-else-if="options.type == 'media'" class="body-box">
|
||||||
|
|||||||
@@ -10,12 +10,15 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
classNames() {
|
classNames() {
|
||||||
return 'fa fa-'+this.icon+((this.margin || this.text && this.text!='')?' push':'')+(this.classes?' '+this.classes:'')
|
return 'fa fa-'+this.icon+((this.margin || this.hasText)?' push':'')+(this.classes?' '+this.classes:'')
|
||||||
|
},
|
||||||
|
hasText() {
|
||||||
|
return this.text && this.text != '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span :title="title"><i :class="classNames"></i><span :class="textClasses">{{ text }}</span></span>
|
<span :title="title"><i :class="classNames"></i><span v-if="hasText" :class="textClasses">{{ text }}</span></span>
|
||||||
</template>
|
</template>
|
||||||
22
src/components/spotIconStack.vue
Normal file
22
src/components/spotIconStack.vue
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<script>
|
||||||
|
import spotIcon from './spotIcon.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
spotIcon
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
iconMain: String,
|
||||||
|
iconMainClasses: String,
|
||||||
|
iconSub: String,
|
||||||
|
iconSubClasses: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span class="drill-icon fa-stack clickable">
|
||||||
|
<spotIcon :icon="iconMain" :classes="'fa-stack-2x clickable'+(iconMainClasses?' '+iconMainClasses:'')" />
|
||||||
|
<spotIcon :icon="iconSub" :classes="'fa-stack-1x'+(iconSubClasses?' '+iconSubClasses:'')" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user