From 1589e5858884b7ab2ccffa2a840262248496d1fc Mon Sep 17 00:00:00 2001 From: Olivier Corradi Date: Tue, 15 Nov 2016 16:36:52 +0100 Subject: [PATCH] Fix #127 --- api/static/app/countryconfig.js | 41 ++++++++ api/static/app/countrytable.js | 6 +- api/static/app/main.js | 177 ++++++++++++++++---------------- api/static/index.html | 35 ++++--- api/static/style.css | 33 +++++- 5 files changed, 183 insertions(+), 109 deletions(-) diff --git a/api/static/app/countryconfig.js b/api/static/app/countryconfig.js index 4bed05b84f..535dfe1950 100644 --- a/api/static/app/countryconfig.js +++ b/api/static/app/countryconfig.js @@ -1,4 +1,45 @@ function addCountriesConfiguration(countries) { + // Names + countries['AT'].fullname = 'Austria'; + countries['BE'].fullname = 'Belgium'; + countries['BG'].fullname = 'Bulgaria'; + countries['BA'].fullname = 'Bosnia and Herzegovina'; + countries['BY'].fullname = 'Belarus'; + countries['CH'].fullname = 'Switzerland'; + countries['CZ'].fullname = 'Czech Republic'; + countries['DE'].fullname = 'Germany'; + countries['DK'].fullname = 'Denmark'; + countries['ES'].fullname = 'Spain'; + countries['EE'].fullname = 'Estonia'; + countries['FI'].fullname = 'Finland'; + countries['FR'].fullname = 'France'; + countries['GB'].fullname = 'United Kingdom'; + countries['GR'].fullname = 'Greece'; + countries['HR'].fullname = 'Croatia'; + countries['HU'].fullname = 'Hungary'; + countries['IE'].fullname = 'Ireland'; + countries['IS'].fullname = 'Iceland'; + countries['IT'].fullname = 'Italy'; + countries['XK'].fullname = 'Kosovo'; + countries['LT'].fullname = 'Lithuania'; + countries['LU'].fullname = 'Luxembourg'; + countries['LV'].fullname = 'Latvia'; + countries['MD'].fullname = 'Moldova'; + countries['MK'].fullname = 'Macedonia (FYROM)'; + countries['ME'].fullname = 'Montenegro'; + countries['NL'].fullname = 'Netherlands'; + countries['NO'].fullname = 'Norway'; + countries['PL'].fullname = 'Poland'; + countries['PT'].fullname = 'Portugal'; + countries['RO'].fullname = 'Romania'; + countries['RU'].fullname = 'Russia'; + countries['RS'].fullname = 'Serbia'; + countries['SK'].fullname = 'Slovakia'; + countries['SI'].fullname = 'Slovenia'; + countries['SE'].fullname = 'Sweden'; + countries['UA'].fullname = 'Ukraine'; + + // Capacities countries['AT'].capacity = { biomass: 393, coal: 819, diff --git a/api/static/app/countrytable.js b/api/static/app/countrytable.js index 7990ac2ce9..fb67471bf4 100644 --- a/api/static/app/countrytable.js +++ b/api/static/app/countrytable.js @@ -233,10 +233,8 @@ CountryTable.prototype.data = function(arg) { // Set header var header = d3.select('.country-table-header'); - header.select('img.country-flag') - .attr('width', 4 * this.FLAG_SIZE_MULTIPLIER) - .attr('height', 3 * this.FLAG_SIZE_MULTIPLIER) - .attr('src', 'libs/flag-icon-css/flags/4x3/' + this._data.countryCode.toLowerCase() + '.svg') + header.select('i#country-flag') + .attr('class', 'flag-icon flag-icon-' + this._data.countryCode.toLowerCase()) header.select('span.country-name') .text(this._data.countryCode); header.select('span.country-last-update') diff --git a/api/static/app/main.js b/api/static/app/main.js index def3ce86c8..8f01343cc7 100644 --- a/api/static/app/main.js +++ b/api/static/app/main.js @@ -21,6 +21,10 @@ var customDate; function isMobile() { return (/android|blackberry|iemobile|ipad|iphone|ipod|opera mini|webos/i).test(navigator.userAgent); } +function isSmallScreen() { + // Should be in sync with media queries in CSS + return screen.width < 600; +} function trackAnalyticsEvent(eventName, paramObj) { if (window.location.href.indexOf("electricitymap") !== -1) { @@ -118,28 +122,37 @@ d3.entries(exchanges).forEach(function(entry) { console.error('Exchange sorted key pair ' + entry.key + ' is not sorted alphabetically'); }); +function selectCountry(countryCode) { + if (!countryCode || !countries[countryCode]) { + // Unselected + d3.select('.country-table-initial-text') + .style('display', 'block'); + countryTable.hide(); + selectedCountryCode = undefined; + } else { + // Selected + console.log(countries[countryCode]); + trackAnalyticsEvent('countryClick', {countryCode: countryCode}); + d3.select('.country-table-initial-text') + .style('display', 'none'); + countryTable + .show() + .data(countries[countryCode]); + selectedCountryCode = countryCode; + } + if (isSmallScreen()) + d3.select('#country-table-back-button').style('display', + selectedCountryCode ? 'block' : 'none'); +} + // Mobile -if (isMobile()) { +if (isSmallScreen()) { d3.select('.map').selectAll('*').remove(); - d3.select('.legend').style('display', 'none'); - - function onSelectedCountryChange() { - var countryCode = d3.select('select.country-picker').node().value; - if (countries[countryCode]) { - d3.select('.country-table-initial-text') - .style('display', 'none'); - countryTable - .show() - .data(countries[countryCode]); - selectedCountryCode = countryCode; - d3.select('select.country-picker').node().selectedIndex = 0; - } - } + d3.select('#country-table-back-button') + .on('click', function() { selectCountry(undefined); }); } else { d3.select('.panel-container') .style('width', 330); - d3.select('.country-picker') - .style('display', 'none'); // Attach event handlers function windMouseOver(coordinates) { @@ -181,7 +194,7 @@ function dataLoaded(err, state, solar, wind) { return; } - if (!isMobile()) { + if (!isSmallScreen()) { if (wind) { console.log('wind', wind); var t_before = moment(wind.forecasts[0][0].header.refTime).add(wind.forecasts[0][0].header.forecastTime, 'hours'); @@ -283,38 +296,34 @@ function dataLoaded(err, state, solar, wind) { }); console.log('countries', countries); - // Populate exchange pairs for arrows - d3.entries(state.exchanges).forEach(function(obj) { - var exchange = exchanges[obj.key]; - if (!exchange) { - console.error('Missing exchange configuration for ' + obj.key); - return; - } - // Copy data - d3.keys(obj.value).forEach(function(k) { - exchange[k] = obj.value[k]; + // Render country picker if we're on mobile + if (isSmallScreen()) { + var validCountries = d3.values(countries).filter(function(d) { + return d.production; + }).sort(function(x, y) { + return d3.ascending(x.fullname || x.countryCode, y.fullname || y.countryCode) }); - }); - console.log('exchanges', exchanges); + var selector = d3.select('.country-picker-container p') + .selectAll('a') + .data(validCountries); + var enterA = selector.enter().append('a'); + enterA + .attr('href', '#') + .append('i') + enterA + .append('text') + selector.select('text') + .text(function(d) { return ' ' + (d.fullname || d.countryCode); }) + selector.select('i') + .attr('class', function(d) { return 'flag-icon flag-icon-' + d.countryCode.toLowerCase(); }) + selector.on('click', function(d) { return selectCountry(d.countryCode); }); + } + // Render country map countryMap .data(d3.values(countries)) - .onSeaClick(function () { - d3.select('.country-table-initial-text') - .style('display', 'block'); - countryTable.hide(); - selectedCountryCode = undefined; - }) - .onCountryClick(function (d, i) { - console.log(d); - trackAnalyticsEvent('countryClick', {countryCode: d.countryCode}); - d3.select('.country-table-initial-text') - .style('display', 'none'); - countryTable - .show() - .data(d); - selectedCountryCode = d.countryCode; - }) + .onSeaClick(function () { selectCountry(undefined); }) + .onCountryClick(function (d) { selectCountry(d.countryCode); }) .onCountryMouseOver(function (d) { d3.select(this) .style('opacity', 0.8) @@ -331,8 +340,8 @@ function dataLoaded(err, state, solar, wind) { tooltip .style('left', (d3.event.pageX - w - 5) + 'px') .style('top', (d3.event.pageY - h - 5) + 'px'); - tooltip.select('img.country-flag') - .attr('src', 'libs/flag-icon-css/flags/4x3/' + d.countryCode.toLowerCase() + '.svg'); + tooltip.select('i.country-flag') + .attr('class', function(d) { return 'flag-icon flag-icon-' + d.countryCode.toLowerCase(); }) tooltip.select('.country-emission-rect') .style('background-color', d.co2intensity ? co2color(d.co2intensity) : 'gray'); tooltip.select('.country-emission-intensity') @@ -349,11 +358,26 @@ function dataLoaded(err, state, solar, wind) { }) .render(); - // Update country table if it is visible - if (selectedCountryCode) - countryTable.data(countries[selectedCountryCode]).render() + // Render country table if it already was visible + if (selectedCountryCode) + countryTable.data(countries[selectedCountryCode]).render() + + if (!isSmallScreen()) { + // Populate exchange pairs for arrows + d3.entries(state.exchanges).forEach(function(obj) { + var exchange = exchanges[obj.key]; + if (!exchange) { + console.error('Missing exchange configuration for ' + obj.key); + return; + } + // Copy data + d3.keys(obj.value).forEach(function(k) { + exchange[k] = obj.value[k]; + }); + }); + console.log('exchanges', exchanges); - if (!isMobile()) + // Render exchanges exchangeLayer .data(d3.values(exchanges)) .projection(countryMap.projection()) @@ -376,16 +400,16 @@ function dataLoaded(err, state, solar, wind) { tooltip.select('.country-emission-rect') .style('background-color', d.co2intensity ? co2color(d.co2intensity) : 'gray'); var i = d.netFlow > 0 ? 0 : 1; - tooltip.select('span.from') + tooltip.select('span#from') .text(d.countryCodes[i]); - tooltip.select('span.to') + tooltip.select('span#to') .text(d.countryCodes[(i + 1) % 2]); - tooltip.select('span.flow') + tooltip.select('span#flow') .text(Math.abs(Math.round(d.netFlow))); - tooltip.select('img.from') - .attr('src', 'libs/flag-icon-css/flags/4x3/' + d.countryCodes[i].toLowerCase() + '.svg'); - tooltip.select('img.to') - .attr('src', 'libs/flag-icon-css/flags/4x3/' + d.countryCodes[(i + 1) % 2].toLowerCase() + '.svg') + tooltip.select('i#from') + .attr('class', 'flag-icon flag-icon-' + d.countryCodes[i].toLowerCase()); + tooltip.select('i#to') + .attr('class', 'flag-icon flag-icon-' + d.countryCodes[(i + 1) % 2].toLowerCase()); }) .onExchangeMouseOut(function (d) { d3.select(this) @@ -397,6 +421,7 @@ function dataLoaded(err, state, solar, wind) { .style('display', 'none'); }) .render(); + } d3.select('.loading') .transition() @@ -457,40 +482,14 @@ function fetchAndReschedule() { document.getElementById('connection-warning').className = "show"; }, 15 * 1000); var Q = queue() - if (isMobile()) { + if (isSmallScreen()) { Q .defer(d3.json, ENDPOINT + '/v1/state'); - if (d3.select('.country-table-initial-text').style() != 'none') { - Q.defer(geolocaliseCountryCode); - } - Q.await(function(err, state, countryCode) { + Q.defer(geolocaliseCountryCode); + Q.await(function(err, state, geolocalisedCountryCode) { handleConnectionError(err); if (!err) { dataLoaded(err, state.data); - if (d3.select('.country-table-initial-text').style() != 'none') { - if (countryCode && countries[countryCode] ) { - // Select one country - d3.select('.country-table-initial-text') - .style('display', 'none'); - countryTable - .show() - .data(countries[countryCode]); - } else { - // Show picker - var countryCodes = d3.entries(countries) - .filter(function (d) { return d.value.production; }) - .map(function (d) { return d.key; }); - countryCodes.unshift('< press to select >'); - var countryOptions = d3.select('select.country-picker') - .selectAll('option') - .data(countryCodes); - countryOptions.enter() - .append('option'); - countryOptions - .text(function(d) { return d }); - countryOptions.exit().remove(); - } - } } setTimeout(fetchAndReschedule, REFRESH_TIME_MINUTES * 60 * 1000); }); @@ -510,7 +509,7 @@ function fetchAndReschedule() { function redraw() { countryTable.render(); - if (!isMobile()) { + if (!isSmallScreen()) { countryMap.render(); co2Colorbar.render(); windColorbar.render(); diff --git a/api/static/index.html b/api/static/index.html index dc72f957ee..11f826768d 100644 --- a/api/static/index.html +++ b/api/static/index.html @@ -19,6 +19,7 @@ Electricity Map | Real-time CO2 emissions of the European electricity production + @@ -111,7 +112,7 @@ -
+
Carbon intensity (gCO2eq/kWh) @@ -123,12 +124,15 @@
-

+

Real-time CO2 emissions of the European electricity production

+

+ +

+
- This map shows in real-time where your electricity comes from and how much CO2 was emitted to produce it.
- It takes into account electricity imports and exports of each country. -
-
- Tip: Click on a country to start exploring ⟶
-
- +

+ This shows in real-time where your electricity comes from and how much CO2 was emitted to produce it.
+ We take into account electricity imports and exports of each country. + +

+

+ You're currently seeing a limited mobile version. For the full experience, visit this page on your computer. +

+

+ Tip: Click on a country to start exploring ⟶ +

+
+

-
Like the map? We would love to hear your feedback!
Found bugs or have ideas? Report them here.

@@ -184,7 +193,7 @@
Cross-border export:
- : MW + : MW
diff --git a/api/static/style.css b/api/static/style.css index 13ea7eccd9..adbe2b5c96 100644 --- a/api/static/style.css +++ b/api/static/style.css @@ -109,6 +109,12 @@ html, body { line-height: 1.7em; pointer-events: all; } +.panel-container h1 { + line-height: 1.3; + color: white; + font-size: 1.6em; + margin: 0px; +} .panel-container > svg { display: block; } @@ -195,7 +201,28 @@ div.fb-share-button, iframe.twitter-share-button, iframe.__slackin { position: absolute; } -.country-flag { - width: 12px; - height: 9px; +@media screen and (min-width: 600px) { + .small-screen-visible { + display: none; + } +} +@media screen and (max-width: 600px) { + .large-screen-visible { + display: none; + } +} + +hr { + background-color: gray; + color: gray; + border: none; + height: 1px; +} + +.country-picker-container p { + column-count: 2; + line-height: 2em; +} +.country-picker-container a { + display: block; }