Commit 49435e45 authored by alain's avatar alain 🐙
Browse files
parents f7757b47 fd02f179
const { editWebpackPlugin, appendWebpackPlugin } = require('@rescripts/utilities') const { editWebpackPlugin, appendWebpackPlugin } = require('@rescripts/utilities')
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
//const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = config => { module.exports = config => {
...@@ -18,6 +19,10 @@ module.exports = config => { ...@@ -18,6 +19,10 @@ module.exports = config => {
config, config,
) )
// config = appendWebpackPlugin(
// new BundleAnalyzerPlugin(),
// config,
// )
if(config.mode === 'production') { if(config.mode === 'production') {
......
export const appSettings = {
language: "nl",
panelBreakpoint: 800
}
\ No newline at end of file
import { color } from "../custom/muv/d3-color" import { color } from "../custom/muv/d3-color"
export const texts = {
pilotLocations: "Pilot locations",
loading: "Loading...",
lastMean: "last hourly average",
lastPeak: "peak value last hour",
mean: "mean",
peak: "peak value"
}
export const mapDefaults = { export const mapDefaults = {
latitude: 46.6957637170966, bounds: [
longitude: 5.765113549054841, [4.5, 52.6],
zoom: 4.5, [5, 52.3]
pitch: 45, ],
bearing: 0 viewport: {
pitch: 60,
bearing: 0
}
} }
export const mapLocations = [ export const mapLocations = [
{ {
name: "Amsterdam", name: "Name",
latitude: 52.371828, bounds: [
longitude: 4.901642, [4.557490, 52.514173],
zoom: 12 [4.694126, 52.436089]
}, ],
{ sublocations: [
name: "Barcelona", {
latitude: 41.40370281706366, name: "Name",
longitude: 2.1432531593864312, bounds: [
zoom: 12 [52.516652, 4.613219].reverse(),
}, [52.476220, 4.700986].reverse()
{ ]
name: "Helsinki", }
latitude: 60.17226969670993, ]
longitude: 24.900654954799048,
zoom: 12
},
{
name: "Palermo",
latitude: 38.12088554193859,
longitude: 13.352510065410891,
zoom: 12
} }
] ]
...@@ -62,6 +47,7 @@ export const mapItemSettings = { ...@@ -62,6 +47,7 @@ export const mapItemSettings = {
sizeMax: 200, sizeMax: 200,
elevationMaxMin: 120, elevationMaxMin: 120,
elevationMaxMax: 2000, elevationMaxMax: 2000,
colorOffline: "#888"
} }
export const lightSettings = { export const lightSettings = {
......
import { addMissingHours } from "../../../src/util/data" import { addMissingDataPoints } from "../../../src/util/data"
export default { export default {
name: "muv", name: "sourcename",
sides: 5, sides: 5,
angle: 0, angle: 0,
factor: 0.5, factor: 0.5,
daysToFetch: 90,
all: async function getAll(parameter) { all: async function(parameter) {
try { try {
const allResponse = await fetch(`https://data.waag.org/api/muv`) const allResponse = await fetch(`https://data.waag.org/api/muv`)
const all = await allResponse.json() const all = await allResponse.json()
//console.log(all)
const latestResponse = await fetch(`https://data.waag.org/api/muv/getOnlineSensors`) const latestResponse = await fetch(`https://data.waag.org/api/muv/getOnlineSensors`)
const latest = await latestResponse.json() const latest = await latestResponse.json()
//console.log(latest)
let offlineSensors = [] let offlineSensors = []
let unregisteredSensors = [] let unregisteredSensors = []
...@@ -29,10 +28,9 @@ export default { ...@@ -29,10 +28,9 @@ export default {
id: sensor.id.toString(), id: sensor.id.toString(),
name: sensor.sensor_type + " " + sensor.id.toString() + " " + sensor.location, name: sensor.sensor_type + " " + sensor.id.toString() + " " + sensor.location,
type: sensor.sensor_type, type: sensor.sensor_type,
source: "muv", source: "sourcename",
coordinates: [sensor.longitude, sensor.latitude].reverse(), coordinates: [sensor.longitude, sensor.latitude].reverse(),
mean: sensorData[`${parameter}`] mean: sensorData[`${parameter}`]
//max: sensorData[`${parameter}_max`]
} }
} else { } else {
offlineSensors.push(sensor.id) offlineSensors.push(sensor.id)
...@@ -65,15 +63,18 @@ export default { ...@@ -65,15 +63,18 @@ export default {
} }
}, },
detail: async function getDetail(station, parameter, start, end) { stationMeta: function(station, parameter) {
const stationMeta = {
type: station.id,
name: station.name,
description: ""
}
return stationMeta
},
stationData: async function(station, parameter, start, end, granularity) {
try { try {
const stationInfo = {
type: station.id,
name: station.name,
description: ""
}
const response = await fetch(`https://data.waag.org/api/muv/getSensorData?sensor_id=${station.id}&start=${start}&end=${end}`) const response = await fetch(`https://data.waag.org/api/muv/getSensorData?sensor_id=${station.id}&start=${start}&end=${end}`)
const responseData = await response.json() const responseData = await response.json()
...@@ -90,7 +91,7 @@ export default { ...@@ -90,7 +91,7 @@ export default {
} }
}) })
const data = addMissingHours(conformedData) const data = addMissingDataPoints(conformedData, granularity)
return { stationInfo, data } return { stationInfo, data }
} catch (error) { } catch (error) {
......
...@@ -8,9 +8,15 @@ export const texts = { ...@@ -8,9 +8,15 @@ export const texts = {
loading: "Bezig met laden...", loading: "Bezig met laden...",
loadingError: "Laden mislukt...", loadingError: "Laden mislukt...",
loadingRetry: "Probeer opnieuw", loadingRetry: "Probeer opnieuw",
nodata: "Geen data...", nodata: "geen data...",
lastMean: "laatste uurgemiddelde", lastMean: "laatste uurgemiddelde",
lastPeak: "piekwaarde laatste uur", lastPeak: "piekwaarde laatste uur",
mean: "gemiddelde", mean: "gemiddelde",
peak: "piekwaarde" peak: "piekwaarde",
downloadData: "download data",
startDate: "startdatum",
endDate: "einddatum",
chartHeaderAddition: "(uurwaarden)",
downloadCsv: "download csv",
downloadInfo: "<small><a class='more' href='https://hollandseluchten.waag.org/data-downloaden/' target='_blank' rel='noopener noreferrer'>meer info over data downloaden en de API</a></small>",
} }
This diff is collapsed.
...@@ -3,33 +3,35 @@ ...@@ -3,33 +3,35 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@deck.gl/aggregation-layers": "^7.3.3", "@deck.gl/aggregation-layers": "^7.3.6",
"@deck.gl/core": "^7.3.3", "@deck.gl/core": "^7.3.6",
"@deck.gl/layers": "^7.3.3", "@deck.gl/layers": "^7.3.6",
"@deck.gl/mapbox": "^7.3.3", "@deck.gl/mapbox": "^7.3.6",
"@deck.gl/react": "^7.3.3", "@deck.gl/react": "^7.3.6",
"d3": "^5.12.0", "d3-color": "^1.4.0",
"d3-scale": "^3.1.0", "d3-interpolate": "^1.4.0",
"d3-scale": "^3.2.1",
"immutable": "^4.0.0-rc.12", "immutable": "^4.0.0-rc.12",
"jsonp": "^0.2.1", "jsonp": "^0.2.1",
"memoize-one": "^5.1.1", "memoize-one": "^5.1.1",
"moment": "^2.24.0", "moment": "^2.24.0",
"moment-range": "^4.0.2", "moment-range": "^4.0.2",
"react": "^16.10.2", "react": "^16.12.0",
"react-dom": "^16.10.2", "react-dom": "^16.12.0",
"react-html-parser": "^2.0.2", "react-html-parser": "^2.0.2",
"react-map-gl": "^5.0.12", "react-map-gl": "^5.1.3",
"react-redux": "^7.1.1", "react-redux": "^7.1.3",
"react-scripts": "^3.2.0", "react-scripts": "^3.2.0",
"recharts": "^1.8.1", "recharts": "^1.8.5",
"redux": "^4.0.4" "redux": "^4.0.4"
}, },
"devDependencies": { "devDependencies": {
"@rescripts/cli": "0.0.12", "@rescripts/cli": "0.0.13",
"@rescripts/rescript-env": "0.0.10", "@rescripts/rescript-env": "0.0.11",
"copy-webpack-plugin": "^5.0.4", "copy-webpack-plugin": "^5.0.5",
"node-sass": "^4.12.0", "node-sass": "^4.13.0",
"redux-devtools": "^3.5.0" "redux-devtools": "^3.5.0",
"webpack-bundle-analyzer": "^3.6.0"
}, },
"scripts": { "scripts": {
"start": "rescripts start", "start": "rescripts start",
......
...@@ -3,7 +3,6 @@ import { connect } from 'react-redux' ...@@ -3,7 +3,6 @@ import { connect } from 'react-redux'
import memoize from "memoize-one"; import memoize from "memoize-one";
import DeckGL from '@deck.gl/react'; import DeckGL from '@deck.gl/react';
import WebMercatorViewport from 'viewport-mercator-project'
import StationLayer from "./StationLayer" import StationLayer from "./StationLayer"
import StationTooltip from "./StationTooltip" import StationTooltip from "./StationTooltip"
...@@ -15,9 +14,11 @@ import IconInfo from "../Modal/IconInfo" ...@@ -15,9 +14,11 @@ import IconInfo from "../Modal/IconInfo"
import { GridLayer } from '@deck.gl/aggregation-layers'; import { GridLayer } from '@deck.gl/aggregation-layers';
import Tooltip from "./Tooltip" import Tooltip from "./Tooltip"
import { doOffsets } from 'util/plot';
class DeckLayers extends React.Component { class DeckLayers extends React.Component {
constructor(props){ constructor(props){
...@@ -30,13 +31,10 @@ class DeckLayers extends React.Component { ...@@ -30,13 +31,10 @@ class DeckLayers extends React.Component {
renderTooltip() { renderTooltip() {
const { hover, hoverContent } = this.state || {} const { hover, hoverContent, mousePosition } = this.state || {}
if(hover) { if(hover) {
const webMercatorViewport = new WebMercatorViewport(this.props.viewport) return <Tooltip position={mousePosition} content={hoverContent} />
const pixelCoords = webMercatorViewport.project(hover.coordinates)
return <Tooltip position={pixelCoords} content={hoverContent} />
} }
} }
...@@ -47,24 +45,30 @@ class DeckLayers extends React.Component { ...@@ -47,24 +45,30 @@ class DeckLayers extends React.Component {
const count = {} const count = {}
Object.keys(deckLayers).forEach(deckLayer => { Object.keys(deckLayers).forEach(deckLayer => {
let previousData = []
Object.keys(deckLayers[deckLayer]).forEach(source => { Object.keys(deckLayers[deckLayer]).forEach(source => {
if(!count[source]) { if(deckLayers[deckLayer][source].layerDefinition && deckLayers[deckLayer][source].layerDefinition.type === 'StationLayer') {
count[source] = { current: 0, total: 0 } if(!count[source]) {
count[source] = { current: 0, total: 0 }
}
count[source].total++
deckLayers[deckLayer][source].data = doOffsets([...previousData, deckLayers[deckLayer][source].data])
previousData.push(deckLayers[deckLayer][source].data)
} }
count[source].total++
}) })
}) })
Object.keys(deckLayers).forEach(deckLayer => { Object.keys(deckLayers).forEach(deckLayer => {
Object.keys(deckLayers[deckLayer]).forEach(sourceName => { Object.keys(deckLayers[deckLayer]).forEach(sourceName => {
const { layerDefinition, source, data } = deckLayers[deckLayer][sourceName] const { layerDefinition, source, data } = deckLayers[deckLayer][sourceName]
count[sourceName].current++
if(!layerDefinition) return if(!layerDefinition) return
count[sourceName].current++
switch(layerDefinition.type) { switch(layerDefinition.type) {
case "StationLayer": case "StationLayer":
const unit = (layerDefinition.units ? layerDefinition.units.find(x => x.id === unitStatus[layerDefinition.id]) : null) const unit = (layerDefinition.units ? layerDefinition.units.find(x => x.id === unitStatus[layerDefinition.id]) : null)
...@@ -91,6 +95,7 @@ class DeckLayers extends React.Component { ...@@ -91,6 +95,7 @@ class DeckLayers extends React.Component {
pickable: true, pickable: true,
extruded: true, extruded: true,
onHover: e => this.setState({ onHover: e => this.setState({
mousePosition: [e.x, e.y],
hover: e.object, hover: e.object,
hoverContent: <StationTooltip d={e.object} label={layerDefinition.label} unit={unit.label} /> hoverContent: <StationTooltip d={e.object} label={layerDefinition.label} unit={unit.label} />
}), }),
...@@ -123,6 +128,7 @@ class DeckLayers extends React.Component { ...@@ -123,6 +128,7 @@ class DeckLayers extends React.Component {
getPosition: d => d.coordinates, getPosition: d => d.coordinates,
//getColor: d => layerDefinition.getColor(d[layerDefinition.getColorKey]), //getColor: d => layerDefinition.getColor(d[layerDefinition.getColorKey]),
onHover: e => this.setState({ onHover: e => this.setState({
mousePosition: [e.x, e.y],
hover: e.object, hover: e.object,
hoverContent: <IconTooltip d={e.object} keys={layerDefinition.keysTooltip} /> hoverContent: <IconTooltip d={e.object} keys={layerDefinition.keysTooltip} />
}), }),
......
...@@ -2,7 +2,7 @@ import { CompositeLayer } from '@deck.gl/core' ...@@ -2,7 +2,7 @@ import { CompositeLayer } from '@deck.gl/core'
import { ColumnLayer } from '@deck.gl/layers' import { ColumnLayer } from '@deck.gl/layers'
import { scaleLinear, scaleLog } from "d3-scale" import { scaleLinear, scaleLog } from "d3-scale"
import { color, getColorArray } from "../util/color.js" import { getColor, getColorArray } from "../util/color.js"
import { setMetersOffset } from "../util/plot" import { setMetersOffset } from "../util/plot"
...@@ -59,44 +59,35 @@ class StationLayer extends CompositeLayer { ...@@ -59,44 +59,35 @@ class StationLayer extends CompositeLayer {
} }
return [ return [
// // max
// new ColumnLayer({
// ...this.props,
// id: `${id}-column-layer-max`,
// data,
// radius
// diskResolution: sides,
// opacity: 1,
// lightSettings,
// getPosition: d => centroid(d.coordinates, d.offset),
// getFillColor: d => getColorArray(lightenBy(color(d.max, legend), 0.25)),
// getElevation: d => elevation(d.max, zoom),
// updateTriggers: {
// getElevation: [zoom],
// getPosition: [radius]
// }
// }),
//mean
new ColumnLayer({ new ColumnLayer({
...this.props, ...this.props,
id: `${id}-column-layer-mean`, id: `${id}-column-layer-mean`,
data, data,
radius, radius,
diskResolution: sides, diskResolution: sides,
opacity: 1, opacity: 0.95,
lightSettings, lightSettings,
getElevation: d => getHeight(d.mean, zoom), getElevation: d => getHeight(d.mean, zoom),
getPosition: d => this.getCoordinates(d, count, radius), getPosition: d => this.getCoordinates(d, count, radius),
getFillColor: d => { getFillColor: d => {
if(d.mean === null) { if(d.mean === null) {
return getColorArray("#ccc") let color = getColorArray(mapItemSettings.colorOffline)
color[3] = color[3] * 0.5
return color
} else { } else {
return getColorArray(color(d.mean, legend, (scale === 'log'))) let color = getColorArray(getColor(d.mean, legend, (scale === 'log')))
//if(d.dataAge > 3) color = changeColorLuminance(color, -0.015 * d.dataAge)
//if(d.dataAge > 4)
color[3] = color[3] * (1 - 0.1 * d.dataAge)
return color
} }
}, },
updateTriggers: { updateTriggers: {
getElevation: [zoom], getElevation: [zoom],
getPosition: [radius] getPosition: [radius, count]
} }
}) })
] ]
......
...@@ -3,27 +3,29 @@ import React from 'react' ...@@ -3,27 +3,29 @@ import React from 'react'
import { texts } from "../../../config/texts" import { texts } from "../../../config/texts"
import { roundBy } from "../util/math.js" import { roundBy } from "../util/math.js"
import { moment } from "../util/time.js"
const SensorTooltip = props => { const SensorTooltip = props => {
const { d, label, unit } = props const { d, label, unit } = props
const dataAgeText = `${ moment(d.timestamp).format("HH") }-${ moment(d.timestamp).add(1, 'hours').format("HH") }${texts.hourShort}`
return ( return (
<div> <div>
<h4>{d.name}</h4> <h4>{d.name}</h4>
<em>{label}</em> <em>{label}</em>
<table className="data"><tbody> <table className="data"><tbody>
{ d.mean !== null && { d.mean !== null &&
<tr> <tr>
<td>{ texts.lastMean }:</td> <td>{`${texts.lastMean} (${dataAgeText}):`}</td>
<td><strong>{ roundBy(d.mean, 1) }{ unit }</strong></td> <td><strong>{ roundBy(d.mean, 1) }{ unit }</strong></td>
</tr> </tr>
} }
{ !!d.max && { d.mean === null &&
<tr> <tr>
<td>{ texts.lastPeak }:</td> <td>{ texts.nodata }</td>
<td><strong>{ roundBy(d.max, 1) }{ unit }</strong></td> </tr>
</tr>
} }
</tbody></table> </tbody></table>
</div> </div>
......
...@@ -8,7 +8,7 @@ class MapAttributions extends React.Component { ...@@ -8,7 +8,7 @@ class MapAttributions extends React.Component {
<div className="mapboxgl-ctrl-bottom-right"> <div className="mapboxgl-ctrl-bottom-right">
<div className="mapboxgl-ctrl mapboxgl-ctrl-attrib"> <div className="mapboxgl-ctrl mapboxgl-ctrl-attrib">
<div className="mapboxgl-ctrl-attrib-inner"> <div className="mapboxgl-ctrl-attrib-inner">
<span>v. 17/10/2019</span> <span>v. 28/11/2019</span>
<span><a href="https://waag.org" target="_blank" rel="noopener noreferrer">waag</a></span> <span><a href="https://waag.org" target="_blank" rel="noopener noreferrer">waag</a></span>
<span<