-
-
Notifications
You must be signed in to change notification settings - Fork 87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Proof of principle] Photos heat map #807
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,13 +30,24 @@ | |
</div> | ||
</LIcon> | ||
</LMarker> | ||
<LHeatMap v-if="points.length >= 50" | ||
ref="heatMap" | ||
:initial-points="points" | ||
:options="{ | ||
// minOpacity: null, | ||
maxZoom: 16, | ||
radius: 50, | ||
blur: 30, | ||
gradient: { 0.4: 'blue', 0.65: 'lime', 1: 'red' }, | ||
}" /> | ||
</LMap> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent } from 'vue'; | ||
import { LMap, LTileLayer, LMarker, LPopup, LIcon } from 'vue2-leaflet'; | ||
import LHeatMap from './map/LHeatMap.vue'; | ||
import { latLngBounds, Icon } from 'leaflet'; | ||
import { IPhoto } from '../../types'; | ||
|
||
|
@@ -73,6 +84,7 @@ Icon.Default.mergeOptions({ | |
export default defineComponent({ | ||
name: 'MapSplitMatter', | ||
components: { | ||
LHeatMap, | ||
LMap, | ||
LTileLayer, | ||
LMarker, | ||
|
@@ -88,6 +100,7 @@ export default defineComponent({ | |
maxBoundsViscosity: 0.9, | ||
}, | ||
clusters: [] as IMarkerCluster[], | ||
points: [], | ||
animMarkers: false, | ||
}), | ||
|
||
|
@@ -137,6 +150,7 @@ export default defineComponent({ | |
if (!reinit) { | ||
this.setBoundsFromQuery(); | ||
} | ||
await this.fetchPoints(); | ||
return await this.fetchClusters(); | ||
} | ||
|
||
|
@@ -245,6 +259,34 @@ export default defineComponent({ | |
this.animateMarkers(); | ||
}, | ||
|
||
async fetchPoints() { | ||
const zoom = "18"; | ||
let minLat = -90; | ||
let maxLat = 90; | ||
let minLon = -180; | ||
let maxLon = 180; | ||
|
||
// Extend bounds by 25% beyond the map | ||
const latDiff = Math.abs(maxLat - minLat); | ||
const lonDiff = Math.abs(maxLon - minLon); | ||
minLat -= latDiff * 0.25; | ||
maxLat += latDiff * 0.25; | ||
minLon -= lonDiff * 0.25; | ||
maxLon += lonDiff * 0.25; | ||
|
||
// Get bounds with expanded margins | ||
const bounds = this.boundsToStr({ minLat, maxLat, minLon, maxLon }); | ||
|
||
// Make API call | ||
const url = API.Q(API.MAP_CLUSTERS(), { bounds, zoom }); | ||
|
||
// Params have changed, quit | ||
const res = await axios.get(url); | ||
this.points = res.data.map((c) => { | ||
return [c.center[0], c.center[1], 1] | ||
}); | ||
Comment on lines
+285
to
+287
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we are loading this current screen only, this might as well be a computed prop derived from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As said in the long response. From my experience the heatmap is capable to handle much more Datapoints than the markers. If this observation holds I think it makes sense to seperate this two data sources. |
||
}, | ||
|
||
boundsFromQuery() { | ||
const bounds = (this.$route.query.b as string).split(','); | ||
return { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
|
||
<template> | ||
<div style="display: none;"> | ||
<slot v-if="ready" /> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import 'leaflet.heat/dist/leaflet-heat.js' | ||
import { findRealParent, propsBinder } from 'vue2-leaflet' | ||
import { DomEvent } from 'leaflet' | ||
|
||
const props = { | ||
initialPoints: { | ||
type: Array, | ||
required: false, | ||
default() { return [] }, | ||
}, | ||
options: { | ||
type: Object, | ||
default() { return {} }, | ||
}, | ||
} | ||
|
||
export default { | ||
props, | ||
data() { | ||
return { | ||
points: null, | ||
ready: false, | ||
} | ||
}, | ||
watch: { | ||
options: { | ||
handler(newOptions) { | ||
this.mapObject.setOptions(newOptions) | ||
}, | ||
deep: true, | ||
}, | ||
points: { | ||
handler(newPoints) { | ||
this.mapObject.setLatLngs(newPoints) | ||
}, | ||
deep: true, | ||
}, | ||
}, | ||
mounted() { | ||
this.points = this.initialPoints | ||
this.mapObject = L.heatLayer(this.points, this.options) | ||
DomEvent.on(this.mapObject, this.$listeners) | ||
propsBinder(this, this.mapObject, props) | ||
this.ready = true | ||
this.parentContainer = findRealParent(this.$parent) | ||
this.parentContainer.addLayer(this) | ||
this.$nextTick(() => { | ||
this.$emit('ready', this.mapObject) | ||
}) | ||
}, | ||
beforeDestroy() { | ||
this.parentContainer.removeLayer(this) | ||
}, | ||
methods: { | ||
addLayer(layer, alreadyAdded) { | ||
if (!alreadyAdded) { | ||
this.mapObject.addLayer(layer.mapObject) | ||
} | ||
}, | ||
removeLayer(layer, alreadyRemoved) { | ||
if (!alreadyRemoved) { | ||
this.mapObject.removeLayer(layer.mapObject) | ||
} | ||
}, | ||
addLatLng(latlng) { | ||
this.mapObject.addLatLng(latlng) | ||
}, | ||
setLatLngs(latlngs) { | ||
this.mapObject.setLatLngs(latlngs) | ||
}, | ||
redraw() { | ||
this.mapObject.redraw() | ||
}, | ||
}, | ||
} | ||
</script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will kill performance on slower servers. It might make sense to change the granularity of what is fetched when the heatmap is enabled, but can't load everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I was thinking of implementing an own endpoint, which just delivers the point array of the appropriate size to cover the World. This response might additionally be cached for some time (Or until a certain number of images was changed.)