1
Fork 0

Show media with thumbnail on map

This commit is contained in:
viktorstrate 2020-09-27 16:20:55 +02:00
parent 0a314b8e1d
commit c73c962fc3
3 changed files with 140 additions and 62 deletions

View File

@ -0,0 +1,66 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import imagePopupSrc from './image-popup.svg'
const Wrapper = styled.div`
width: 56px;
height: 68px;
position: relative;
margin-top: -54px;
`
const ThumbnailImage = styled.img`
position: absolute;
width: 48px;
height: 48px;
top: 4px;
left: 4px;
border-radius: 2px;
object-fit: cover;
`
const PopupImage = styled.img`
width: 100%;
height: 100%;
`
const PointCountCircle = styled.div`
position: absolute;
top: -10px;
right: -10px;
width: 24px;
height: 24px;
background-color: #00b3dc;
border-radius: 50%;
color: white;
text-align: center;
padding-top: 2px;
`
const MapClusterMarker = ({
thumbnail: thumbJson,
point_count_abbreviated,
cluster,
}) => {
const thumbnail = JSON.parse(thumbJson)
return (
<Wrapper>
<PopupImage src={imagePopupSrc} />
<ThumbnailImage src={thumbnail.url} />
{cluster && (
<PointCountCircle>{point_count_abbreviated}</PointCountCircle>
)}
</Wrapper>
)
}
MapClusterMarker.propTypes = {
thumbnail: PropTypes.string,
cluster: PropTypes.bool,
point_count_abbreviated: PropTypes.number,
}
export default MapClusterMarker

View File

@ -1,4 +1,6 @@
import React, { useEffect, useRef, useState } from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import { useQuery } from 'react-apollo'
import gql from 'graphql-tag'
import styled from 'styled-components'
@ -6,7 +8,8 @@ import styled from 'styled-components'
import 'mapbox-gl/dist/mapbox-gl.css'
import Layout from '../../Layout'
import imagePopup from './image-popup.png'
import MapClusterMarker from './MapClusterMarker'
const MapWrapper = styled.div`
width: 100%;
@ -66,74 +69,62 @@ const MapPage = () => {
type: 'geojson',
data: mapboxData.myMediaGeoJson,
cluster: true,
clusterMaxZoom: 14, // Max zoom to cluster points on
// clusterMaxZoom: 14, // Max zoom to cluster points on
clusterRadius: 50,
clusterProperties: {
thumbnail_url: [
'coalesce',
['get', 'url', ['get', 'thumbnail']],
false,
],
thumbnail: ['coalesce', ['get', 'thumbnail'], false],
},
})
map.current.loadImage(imagePopup, (error, image) => {
console.log(error, image)
map.current.addImage('media-popup-bg', image)
map.current.addLayer({
id: 'media-cluster-popup',
type: 'symbol',
source: 'media',
filter: ['has', 'point_count'],
layout: {
'icon-image': 'media-popup-bg',
'icon-size': 0.5,
'icon-allow-overlap': true,
},
})
map.current.addLayer({
id: 'media-cluster-count-bg',
type: 'circle',
source: 'media',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#11b4da',
'circle-radius': 11,
'circle-translate': [22, -24],
},
})
map.current.addLayer({
id: 'media-cluster-count',
type: 'symbol',
source: 'media',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': 12,
'text-allow-overlap': true,
'text-offset': [22 / 12, -24 / 12],
},
paint: {
'text-color': '#ffffff',
},
})
// Add dummy layer for features to be queryable
map.current.addLayer({
id: 'media-points',
type: 'circle',
source: 'media',
filter: ['!', true],
})
// map.current.addLayer({
// id: 'media-points',
// type: 'circle',
// source: 'media',
// filter: ['!', ['has', 'point_count']],
// paint: {
// 'circle-color': '#11b4da',
// 'circle-radius': 4,
// 'circle-stroke-width': 1,
// 'circle-stroke-color': '#fff',
// },
// })
map.current.on('move', updateMarkers)
map.current.on('moveend', updateMarkers)
updateMarkers()
var markers = {}
var markersOnScreen = {}
function updateMarkers() {
var newMarkers = {}
var features = map.current.querySourceFeatures('media')
// for every media on the screen, create an HTML marker for it (if we didn't yet),
// and add it to the map if it's not there already
for (var i = 0; i < features.length; i++) {
var coords = features[i].geometry.coordinates
var props = features[i].properties
var id = props.cluster ? props.cluster_id : props.media_id
var marker = markers[id]
if (!marker) {
var el = createClusterPopupElement(props)
marker = markers[id] = new mapboxLibrary.Marker({
element: el,
}).setLngLat(coords)
}
newMarkers[id] = marker
if (!markersOnScreen[id]) marker.addTo(map.current)
}
// for every marker we've added previously, remove those that are no longer visible
for (id in markersOnScreen) {
if (!newMarkers[id]) markersOnScreen[id].remove()
}
markersOnScreen = newMarkers
}
function createClusterPopupElement(props) {
const el = document.createElement('div')
ReactDOM.render(<MapClusterMarker {...props} />, el)
return el
}
console.log(map.current)
})

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 56 68" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.875,0,0,1.0625,0,0)">
<g transform="matrix(1,0,0,0.970588,0,0)">
<g transform="matrix(0.857143,0,0,1.55496,8,-35.483)">
<g style="filter:url(#_Effect1);">
<ellipse cx="28" cy="61.172" rx="8" ry="0.828" style="fill-opacity:0.5;"/>
</g>
</g>
<g transform="matrix(1.14286,0,0,0.969697,0,0)">
<path d="M4,54C3.47,54 2.961,53.789 2.586,53.414C2.211,53.039 2,52.53 2,52C2,43.732 2,12.268 2,4C2,3.47 2.211,2.961 2.586,2.586C2.961,2.211 3.47,2 4,2L52,2C52.53,2 53.039,2.211 53.414,2.586C53.789,2.961 54,3.47 54,4L54,52C54,52.53 53.789,53.039 53.414,53.414C53.039,53.789 52.53,54 52,54C48.077,54 39.521,54 36.828,54C36.298,54 35.789,54.211 35.414,54.586C34.161,55.839 31.192,58.808 29.414,60.586C29.039,60.961 28.53,61.172 28,61.172C27.47,61.172 26.961,60.961 26.586,60.586C24.808,58.808 21.839,55.839 20.586,54.586C20.211,54.211 19.702,54 19.172,54C16.479,54 7.923,54 4,54Z" style="fill:white;"/>
</g>
</g>
</g>
<defs>
<filter id="_Effect1" filterUnits="userSpaceOnUse" x="8" y="54.7306" width="40" height="12.8819">
<feGaussianBlur in="SourceGraphic" stdDeviation="1.1983"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB