diff options
| -rw-r--r-- | client/chart/pie.charts.js | 2 | ||||
| -rw-r--r-- | client/index.js | 32 | ||||
| -rw-r--r-- | client/map/index.js | 302 | ||||
| -rw-r--r-- | client/table/citations.table.js | 10 | ||||
| -rw-r--r-- | site/assets/css/css.css | 19 |
5 files changed, 217 insertions, 148 deletions
diff --git a/client/chart/pie.charts.js b/client/chart/pie.charts.js index c3b94b7b..939e9262 100644 --- a/client/chart/pie.charts.js +++ b/client/chart/pie.charts.js @@ -17,7 +17,7 @@ class PieCharts extends Component { render() { const { payload } = this.props const { paper, citations } = payload.data - console.log(this.props) + // console.log(this.props) if (!citations.length) return null const countries = {} diff --git a/client/index.js b/client/index.js index c003a8b3..1a80e74f 100644 --- a/client/index.js +++ b/client/index.js @@ -2,7 +2,7 @@ import React from 'react' import ReactDOM from 'react-dom' import { AppContainer } from 'react-hot-loader' import { Provider } from 'react-redux' -import 'waypoints/lib/noframework.waypoints.min.js'; +import 'waypoints/lib/noframework.waypoints.min.js' import { toArray } from './util' import Applet from './applet' @@ -62,7 +62,7 @@ function runApplets() { let payload try { payload = JSON.parse(el.dataset.payload) - console.log(payload) + // console.log(payload) } catch (e) { return null } @@ -106,7 +106,7 @@ function runApplets() { } payload.dataset = dataset payload.url = url - console.log(payload) + // console.log(payload) return [el, payload] }).filter(a => !!a) const withDataset = applets.map(a => a[1].dataset ? a[1] : null).filter(a => !!a) @@ -122,20 +122,20 @@ function runApplets() { function buildWaypoints() { const element = document.querySelector('.content section:nth-child(2)') - if (element) { - var waypoint = new Waypoint({ - element, - handler: function(direction) { - if (direction === 'down') { - document.body.classList.add('scrolled') - } else { - document.body.classList.remove('scrolled') - } - // console.log(direction) - // console.log('Scrolled to waypoint!') + if (!element) return null + let waypoint = new Waypoint({ + element, + handler: function(direction) { + if (direction === 'down') { + document.body.classList.add('scrolled') + } else { + document.body.classList.remove('scrolled') } - }) - } + // console.log(direction) + // console.log('Scrolled to waypoint!') + } + }) + return waypoint } function main() { diff --git a/client/map/index.js b/client/map/index.js index 29fc2286..4492a69a 100644 --- a/client/map/index.js +++ b/client/map/index.js @@ -28,7 +28,9 @@ const arcStyles = { }, } +const buttonOrder = ['edu', 'company', 'gov'] const sortOrder = ['edu', 'company', 'gov', 'mil'] +const typeScores = { all: 0, edu: 1, company: 2, gov: 3, mil: 3 } const redDot = L.icon({ iconUrl: '/assets/img/reddot.png', @@ -37,145 +39,199 @@ const redDot = L.icon({ popupAnchor: [0, -5] // point from which the popup should open relative to the iconAnchor }) -function addMarker(map, latlng, citations) { - const marker = L.marker(latlng, { icon: redDot }).addTo(map) - let message = citations.map(citation => { - const { title, addresses, year, pdf, doi } = citation - let rec = [ - "<b>", title, "</b>", - ] - if (pdf && pdf.length) { - rec.unshift("<a href='" + pdf[0] + "' target='_blank'>") - rec.push("</a>") - } - else if (doi && doi.length) { - rec.unshift("<a href='" + doi[0] + "' target='_blank'>") - rec.push("</a>") +class Map { + constructor(el, data) { + let { paper, citations } = data + this.el = el + this.paper = paper + this.markers = [] + this.arcs = [] + this.filter = typeScores.all + + if (paper.addresses && paper.addresses.length) { + this.source = [ + paper.addresses[0].lat, + paper.addresses[0].lng + ].map(n => (parseFloat(n) || 0)) + } else { + console.error("No address found for root paper") + this.source = [0, 0] + // console.log(data) } - if (year) { - rec.push(" (" + year + ")") + + // group papers by address + this.citationsByAddress = {} + this.citations = citations + this.citations.forEach(citation => { + if (!citation.addresses) { + return + } + citation.addresses.forEach(address => { + if (!(address.name in this.citationsByAddress)) { + this.citationsByAddress[address.name] = { address, citations: [] } + } + this.citationsByAddress[address.name].citations.push(citation) + }) + }) + this.build() + this.bind() + this.buildMarkers() + this.rootMarker.openPopup() + } + + build() { + this.map = L.map(this.el).setView([25, 0], 2) + L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', { + attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' + + 'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', + maxZoom: 18, + id: 'mapbox.dark', + style: 'mapbox://styles/mapbox/dark-v9', + accessToken: 'pk.eyJ1IjoiZmFuc2FsY3kiLCJhIjoiY2pvN3I1czJwMHF5NDNrbWRoMWpteHlrdCJ9.kMpM5syQUhVjKkn1iVx9fg' + }).addTo(this.map) + } + + bind() { + // a transparent div to cover the map, so normal scroll events will not be eaten by leaflet + const mapCover = document.createElement("div") + mapCover.classList.add("map_cover") + mapCover.innerHTML = "<div class='cover_message'>Click here to explore the map</div>" + mapCover.querySelector('div').addEventListener('click', () => { + this.map.scrollWheelZoom.enable() + if (mapCover.parentNode === this.el) { + this.el.removeChild(mapCover) + } + }) + mapCover.querySelector('div').addEventListener('touchstart', (e) => { + e.preventDefault() + }) + mapCover.querySelector('div').addEventListener('tap', () => { + this.map.scrollWheelZoom.enable() + if (mapCover.parentNode === this.el) { + this.el.removeChild(mapCover) + } + }) + function stopPropagation(e) { + e.stopPropagation() } - const addressString = addresses.map(addr => addr.name).join('<br/>') - rec.push("<br>") - rec.push(addressString) - return rec.join("") - }) + mapCover.addEventListener('mousewheel', stopPropagation, true) + mapCover.addEventListener('DOMMouseScroll', stopPropagation, true) - marker.bindPopup(message.join('<br><br>')) - return marker -} + this.map.scrollWheelZoom.disable() + this.map.on('focus', () => { + this.map.scrollWheelZoom.enable() + if (mapCover.parentNode === this.el) { + this.el.removeChild(mapCover) + } + }) + this.map.on('blur', () => { + this.map.scrollWheelZoom.disable() + // el.appendChild(mapCover) + }) -function addArc(map, src, dest, arcStyle) { - L.bezier({ - path: [ - [ - { lat: src[0], lng: src[1] }, - { lat: dest[0], lng: dest[1] }, - ], - ] - }, arcStyle).addTo(map) -} + this.el.appendChild(mapCover) -export default function append(el, payload) { - const { data } = payload - if (!data) return - let { paper, citations } = data - let source = [0, 0] + buttonOrder.forEach(type => { + const typeClass = type.substr(0, 3) + const el = document.querySelector('.map-legend .' + typeClass) + el.addEventListener('click', () => { + console.log(el) + this.filterMarkers(el, type) + }) + }) + } - let map = L.map(el).setView([25, 0], 2) - L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', { - attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + - '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' + - 'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', - maxZoom: 18, - id: 'mapbox.dark', - style: 'mapbox://styles/mapbox/dark-v9', - accessToken: 'pk.eyJ1IjoiZmFuc2FsY3kiLCJhIjoiY2pvN3I1czJwMHF5NDNrbWRoMWpteHlrdCJ9.kMpM5syQUhVjKkn1iVx9fg' - }).addTo(map) + filterMarkers(el, type) { + const active = document.querySelector('.map-legend .active') + if (active) active.classList.remove('active') + const newFilter = typeScores[type] + if (this.filter === newFilter) { + this.filter = typeScores.all + } else { + this.filter = newFilter + el.classList.add('active') + } + this.buildMarkers() + } - if (paper.addresses && paper.addresses.length) { - source = [paper.addresses[0].lat, paper.addresses[0].lng].map(n => (parseFloat(n) || 0)) - } else { - console.error("No address found for root paper") - // console.log(data) + resetMarkers() { + this.arcs.forEach(arc => arc.remove()) + this.markers.forEach(marker => marker.remove()) + this.markers = [] + this.arcs = [] } - // group papers by address - let citationsByAddress = {} - citations.forEach(citation => { - if (!citation.addresses) { + buildMarkers() { + this.resetMarkers() + Object.keys(this.citationsByAddress).map(name => { + const { citations: citationList, address } = this.citationsByAddress[name] + if (this.filter && typeScores[address.type] !== this.filter) return + // console.log(name, citationsByAddress[name]) // console.log(citation) - return - } - // console.log(citation) - citation.addresses.forEach(address => { - if (!(address.name in citationsByAddress)) { - citationsByAddress[address.name] = { address, citations: []} + const latlng = [address.lat, address.lng].map(n => parseFloat(n)) + if (Number.isNaN(latlng[0]) || Number.isNaN(latlng[1])) return + this.addMarker(latlng, citationList) + const style = { ...arcStyles[address.type] } + let weight = Math.min(citationList.length, 5) + let opacity = 0.5 + Math.min(citationList.length / 5, 0.5) + if (address.type !== 'edu') { + weight += 1 + opacity = 1 } - citationsByAddress[address.name].citations.push(citation) + style.weight = String(weight) + style.opacity = opacity + this.addArc(this.source, latlng, style) }) - }) - Object.keys(citationsByAddress).map(name => { - const { citations: citationList, address: citationAddress } = citationsByAddress[name] - // console.log(name, citationsByAddress[name]) - // console.log(citation) - const latlng = [citationAddress.lat, citationAddress.lng].map(n => parseFloat(n)) - if (Number.isNaN(latlng[0]) || Number.isNaN(latlng[1])) return - addMarker(map, latlng, citationList) - const style = { ...arcStyles[citationAddress.type] } - let weight = Math.min(citationList.length, 5) - let opacity = 0.5 + Math.min(citationList.length / 5, 0.5) - if (citationAddress.type !== 'edu') { - weight += 1 - opacity = 1 - } - style.weight = String(weight) - style.opacity = opacity - addArc(map, source, latlng, style) - }) - - // console.log(paper) + this.rootMarker = this.addMarker(this.source, [this.paper]) + } - const rootMarker = addMarker(map, source, [paper]) - rootMarker.openPopup() + addMarker(latlng, citations) { + const marker = L.marker(latlng, { icon: redDot }).addTo(this.map) + let message = citations.map(citation => { + const { title, addresses, year, pdf, doi } = citation + let rec = [ + "<b>", title, "</b>", + ] + if (pdf && pdf.length) { + rec.unshift("<a href='" + pdf[0] + "' target='_blank'>") + rec.push("</a>") + } + else if (doi && doi.length) { + rec.unshift("<a href='" + doi[0] + "' target='_blank'>") + rec.push("</a>") + } + if (year) { + rec.push(" (" + year + ")") + } + const addressString = addresses.map(addr => addr.name).join('<br/>') + rec.push("<br>") + rec.push(addressString) + return rec.join("") + }) - // a transparent div to cover the map, so normal scroll events will not be eaten by leaflet - const mapCover = document.createElement("div") - mapCover.classList.add("map_cover") - mapCover.innerHTML = "<div class='cover_message'>Click here to explore the map</div>" - mapCover.querySelector('div').addEventListener('click', () => { - map.scrollWheelZoom.enable() - if (mapCover.parentNode === el) { - el.removeChild(mapCover) - } - }) - mapCover.querySelector('div').addEventListener('touchstart', (e) => { - e.preventDefault() - }) - mapCover.querySelector('div').addEventListener('tap', () => { - map.scrollWheelZoom.enable() - if (mapCover.parentNode === el) { - el.removeChild(mapCover) - } - }) - function stopPropagation(e) { - e.stopPropagation() + marker.bindPopup(message.join('<br><br>')) + this.markers.push(marker) + return marker } - mapCover.addEventListener('mousewheel', stopPropagation, true) - mapCover.addEventListener('DOMMouseScroll', stopPropagation, true) - map.scrollWheelZoom.disable() - map.on('focus', () => { - map.scrollWheelZoom.enable() - if (mapCover.parentNode === el) { - el.removeChild(mapCover) - } - }) - map.on('blur', () => { - map.scrollWheelZoom.disable() - // el.appendChild(mapCover) - }) + addArc(src, dest, arcStyle) { + const arc = L.bezier({ + path: [ + [ + { lat: src[0], lng: src[1] }, + { lat: dest[0], lng: dest[1] }, + ], + ] + }, arcStyle).addTo(this.map) + this.arcs.push(arc) + } +} - el.appendChild(mapCover) +export default function append(el, payload) { + const { data } = payload + if (!data) return + let mapContainer = new Map(el, data) + return mapContainer } diff --git a/client/table/citations.table.js b/client/table/citations.table.js index 178cc65b..8fe46b69 100644 --- a/client/table/citations.table.js +++ b/client/table/citations.table.js @@ -24,18 +24,20 @@ class CitationsTable extends Component { filteredCitations: [], } - componentDidMount(){ + componentDidMount() { this.updateCitations() } - componentDidUpdate(oldProps){ + + componentDidUpdate(oldProps) { if (this.props.payload.data.citations !== oldProps.payload.data.citations) { this.updateCitations() } } - updateCitations(){ + + updateCitations() { const { paper, citations } = this.props.payload.data if (!citations.length) this.setState({ formattedCitations: [] }) - console.log(citations.filter(a => a.title.match('Coarse'))) + // console.log(citations.filter(a => a.title.match('Coarse'))) const formattedCitations = citations.sort((a,b) => a.title.localeCompare(b.title)).map(citation => { const pdf_link = (citation.pdf && citation.pdf.length) ? citation.pdf[0] diff --git a/site/assets/css/css.css b/site/assets/css/css.css index 12b059ef..9830e96d 100644 --- a/site/assets/css/css.css +++ b/site/assets/css/css.css @@ -861,6 +861,9 @@ section.fullwidth .image { .desktop .dataset-list .sort-options li:hover { background: #888; } +.desktop .dataset-list .sort-options li.active:hover { + background: #fff; +} .dataset_list .applet { margin-bottom: 15px; } @@ -954,13 +957,17 @@ page-specific formatting ul.map-legend{ display: inline-block; - margin:0; - font-size:14px; + margin: 0; + font-size: 14px; } -ul.map-legend li{ +ul.map-legend li { margin-right: 10px; - padding-top:4px; + padding-top: 4px; display: inline-block; + cursor: pointer; +} +.desktop ul.map-legend li:hover { + text-decoration: underline; } ul.map-legend li:before { content: ''; @@ -969,6 +976,10 @@ ul.map-legend li:before { height: 10px; margin-right: 6px; } +ul.map-legend li.active { + text-decoration: underline; + color: #fff; +} ul.map-legend li.edu:before { background-color: #f2f293; } |
