Show media with thumbnail on map
This commit is contained in:
parent
0a314b8e1d
commit
c73c962fc3
|
@ -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
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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 |
Loading…
Reference in New Issue