Skip to content

Commit

Permalink
Merge pull request #61 from developmentseed/develop
Browse files Browse the repository at this point in the history
Misc improvements, documentation updates and bump to major version
  • Loading branch information
ricardoduplos authored May 20, 2021
2 parents 5c746ef + 58e5325 commit a59a9b8
Show file tree
Hide file tree
Showing 19 changed files with 196 additions and 22 deletions.
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Africa Electrification Platform
The Africa Electrification Platform showcases geospatial electrification analysis conducted by the GEPAR team at the World Bank.

* interested in configuring a study? Check the [/docs](/docs) section
* want to run the project locally? Check [DEVELOPMENT](/DEVELOPMENT.md)
Explore electrification planning activities in the Africa Region, interact with country data and browse different scenario results. The program is funded by ESMAP at the World Bank.

![Study Configuration](docs/media/splash.png)

## Documentation

* Want to run the project locally? Check the [development guidelines](/DEVELOPMENT.md);
* Interested in configuring a study or updating static content? Visit the [documentation](/docs);
* To upload a dataset to Energydata.info, so it can be used in this tool, please check [these steps](/docs/DATASETS.md).

## License

This project is licensed under **The MIT License (MIT)**, see the [LICENSE](LICENSE) file for more details.


6 changes: 6 additions & 0 deletions content/study/posts/liberia.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
title: Liberia National Electrification Analysis
bbox: [[-14.3691,4.2762], [-4.2258,8.7002]]
external: https://liberianea.com/
country: Liberia
study:
period: 2020
30 changes: 30 additions & 0 deletions docs/DATASETS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Dataset Preparation at Energydata.info
## Adding layers
Before adding a layer on the selected study it is necessary to prepare the layer data in the proper web format. Follow the steps below.

1) Select the particular resource that you want to include on the study:

![Dataset preparation](media/dataset-prep-01.png)

2) Check if the resource have the proper layer format, otherwise "Refresh Vector Data" to convert to the proper web format.

![Dataset preparation](media/dataset-prep-02.png)

The current list of source vectortiles formats supported is: shp, zip, shpzip and broken geojson. The converter apply, based on the file size:

- GeoJSON non-wgs84 -> GeoJSON wgs84 (small size)
- GeoJSON non-wgs84 -> tiles images (big size)

3) Once converted you will receive a message informing that the process is done and you can check the new resource created in the web layer format:

![Dataset preparation](media/dataset-prep-03.png)

![Dataset preparation](media/dataset-prep-04.png)

You can always check back the original resource by clicking on "Original Format":

![Dataset preparation](media/dataset-prep-05.png)

4) Now you can copy the URL from Download button, with the converted layer end point to be used to Add a New Layer in to the Config File

![Dataset preparation](media/dataset-prep-06.png)
18 changes: 18 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ The study configuration consists of two files:

* a `yml` file that contains the basic information and metadata of the study.
* a `json` file that contains the map configuration.

> 🌍 AEP also supports external studies. See the [External studies](#external-studies) section on how to configure them.
![Study Configuration](media/study-main-page.png)

Expand Down Expand Up @@ -62,6 +64,20 @@ The main information and metadata of each study is managed through a `yml` file,
| layers[].displayData[].labelProp | `string` | A dynamic label for the popover. Exclusive with `label` |
| layers[].displayData[].valueProp | `string` | A dynamic value for the popover. Exclusive with `value` |

## External studies
External studies can be used to include a reference to external content in the studies hub.
They will be rendered with an iconographic indication that their content is outside the AEP application.

![](./media/external-study-card.png)

An external study only has one `yml` file which must contain a `title`, `bbox`, and an `external` property with the Url to link to.
Example:
```yml
title: Liberia National Electrification Analysis
bbox: [[-14.3691,4.2762], [-4.2258,8.7002]]
external: https://liberianea.com/
```
## Map configuration
The map of each study is configured using a `json` file that follows the Mapbox Style specification. For a full example, please see [`kenya-mb.json`](/content/study/posts/kenya-mb.json).

Expand Down Expand Up @@ -99,6 +115,8 @@ yarn validate
5. set up a Pull Request and merge once [the validations](#validating-configuration) are run successfully

## Add a layer
If you have data to upload to [Energydata.info](https://energydata.info/), please follow the steps described in the [datasets preparation document](./DATASETS.md), prior to adding to AEP.

To add a new layer to a study that users can interact with, requires three things:

1. add a source to the Mapbox Style Specification that references the dataset. This example shows a GeoJSON, but other types like raster or vector tiles are also supported.
Expand Down
Binary file added docs/media/dataset-prep-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/dataset-prep-02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/dataset-prep-03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/dataset-prep-04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/dataset-prep-05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/dataset-prep-06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/external-study-card.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ exports.createSchemaCustomization = ({ actions, schema }) => {
name: 'PostsYaml',
fields: {
title: 'String',
external: 'String',
bbox: 'JSON',
zoomExtent: 'JSON',
country: 'String',
Expand Down Expand Up @@ -120,7 +121,10 @@ exports.createPages = async ({ graphql, actions }) => {
const result = await graphql(
`
{
allPostsYaml {
allPostsYaml(
sort: { fields: title }
filter: { external: { eq: null } }
) {
nodes {
id
fields {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aep-frontend",
"version": "0.5.0",
"version": "1.0.0",
"description": "Africa Electrification Platform",
"repository": {
"type": "git",
Expand Down
18 changes: 15 additions & 3 deletions schema/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const yml = require('js-yaml');
const studyFileExists = (val) =>
fs.existsSync(path.join(__dirname, '../content/study/posts/', val));

const studySchema = new Schema({
const baseSchema = {
title: { type: String, required: true },
bbox: [
[
Expand All @@ -21,7 +21,16 @@ const studySchema = new Schema({
{ type: Number, required: true },
{ type: Number, required: true }
]
],
]
};

const externalStudySchema = new Schema({
...baseSchema,
external: { type: String, match: /^https?:\/\// }
});

const studySchema = new Schema({
...baseSchema,
zoomExtent: [{ type: Number }, { type: Number }],
mapConfig: { type: String, use: { studyFileExists }, required: true },
study: {
Expand Down Expand Up @@ -132,7 +141,10 @@ function printResult(fn, fileErrors) {
console.log('\nValidating YML\n===');
ymlFiles.forEach((fn) => {
const fileContent = yml.load(fs.readFileSync(fn));
const fileErrors = studySchema.validate(fileContent, { strip: false });
const schemaToUse = fileContent.external
? externalStudySchema
: studySchema;
const fileErrors = schemaToUse.validate(fileContent, { strip: false });

if (fileErrors.length) {
errors = true;
Expand Down
22 changes: 22 additions & 0 deletions src/components/smart-link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import T from 'prop-types';
import { Link } from 'gatsby';

/**
* Switches between a `a` and a `Link` depending on the url.
*/
const SmartLink = (props) => {
const { to, ...rest } = props;

return /^https?:\/\//.test(to) ? (
<a href={to} {...rest} />
) : (
<Link to={to} {...rest} />
);
};

SmartLink.propTypes = {
to: T.string
};

export default SmartLink;
54 changes: 49 additions & 5 deletions src/pages/studies/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import styled from 'styled-components';
import { glsp, media, themeVal } from '@devseed-ui/theme-provider';
import Layout from '../../components/layout';
import { ContentBlock } from '../../styles/content-block';
import Prose from '../../styles/typography/prose';

import {
Inpage,
Expand All @@ -25,6 +26,22 @@ import {
CardMediaThumb
} from '../../styles/card';

const StudiesIntro = styled.div`
grid-column: content-start / content-end;
${media.smallUp`
grid-column: content-start / content-end;
`}
${media.mediumUp`
grid-column: content-start / content-end;
`}
${media.largeUp`
grid-column: content-start / content-9;
`}
`;

const StudiesList = styled.ul`
grid-column: content-start / content-end;
display: grid;
Expand Down Expand Up @@ -52,6 +69,8 @@ const StudiesList = styled.ul`
export default function Studies({ data }) {
const studies = data.allPostsYaml.nodes;

const hasExternal = studies.some((s) => !!s.external);

return (
<Layout title='Studies'>
<Inpage>
Expand All @@ -62,13 +81,37 @@ export default function Studies({ data }) {
</InpageHeader>
<InpageBody>
<ContentBlock>
<StudiesIntro>
<Prose>
<h2>Browse the studies</h2>
<p>
There {studies.length === 1 ? 'is' : 'are'} currently{' '}
<strong>
{studies.length}{' '}
{studies.length === 1 ? 'study' : 'studies'}
</strong>{' '}
available.{' '}
{hasExternal &&
'Studies marked as external are hosted outside the platform.'}
</p>
</Prose>
</StudiesIntro>
<StudiesList>
{studies.map((node) => (
<li key={node.id}>
<CardInteractive
linkTo={`/studies/${node.fields.slug}`}
linkTitle='View study'
linkLabel='View'
linkProps={{
to: node.external
? node.external
: `/studies/${node.fields.slug}`,
title: node.external
? 'View external study'
: 'View study',
target: node.external ? '_blank' : undefined,
rel: node.external ? 'noopener noreferrer' : undefined
}}
linkLabel={node.external ? 'View external study' : 'View'}
isExternal={!!node.external}
>
<CardHeader>
<CardHeadline>
Expand All @@ -81,7 +124,7 @@ export default function Studies({ data }) {
<dd>{node.country}</dd>
</>
)}
{node.study.period && (
{node.study?.period && (
<>
<dt>Period</dt>
<dd>{node.study.period}</dd>
Expand Down Expand Up @@ -129,10 +172,11 @@ export const pageQuery = graphql`
}
}
}
allPostsYaml {
allPostsYaml(sort: { fields: title }) {
nodes {
id
title
external
bbox
country
study {
Expand Down
40 changes: 35 additions & 5 deletions src/styles/card.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useState } from 'react';
import T from 'prop-types';
import styled, { css } from 'styled-components';
import { Link } from 'gatsby';

import { glsp, media, multiply, themeVal } from '@devseed-ui/theme-provider';
import { Heading } from '@devseed-ui/typography';
import { headingAlt } from '@devseed-ui/typography';
import collecticon from '@devseed-ui/collecticons';
import SmartLink from '../components/smart-link';

export const Card = styled.article`
position: relative;
Expand All @@ -29,6 +30,29 @@ export const Card = styled.article`
z-index: 3;
pointer-events: none;
}
${({ isExternal }) =>
isExternal &&
css`
&::after {
${collecticon('expand-top-right')}
position: absolute;
top: 0;
right: 0;
z-index: 4;
display: flex;
justify-content: flex-end;
align-items: flex-start;
width: 3.5rem;
height: 3.5rem;
color: ${themeVal('color.surface')};
line-height: 1;
background: ${themeVal('color.link')};
padding: ${glsp(0.5)};
clip-path: polygon(100% 0, 0 0, 100% 100%);
pointer-events: none;
}
`}
`;

export const CardHeader = styled.header`
Expand All @@ -41,7 +65,9 @@ export const CardHeader = styled.header`
`}
`;

export const CardHeadline = styled.div``;
export const CardHeadline = styled.div`
/* styled-component */
`;

export const CardTitle = styled(Heading)`
font-size: 1rem;
Expand Down Expand Up @@ -82,7 +108,8 @@ export const CardMedia = styled.figure`

export const CardMediaThumb = styled.div`
position: relative;
background: ${themeVal('color.baseAlphaA')};
min-height: 3rem;
background: ${themeVal('color.baseAlphaB')};
border-radius: ${themeVal('shape.rounded')};
overflow: hidden;
Expand All @@ -109,7 +136,7 @@ export const CardMediaThumb = styled.div`
}
`;

export const CardLink = styled(Link)`
export const CardLink = styled(SmartLink)`
position: absolute;
z-index: 1;
top: 0;
Expand Down Expand Up @@ -170,6 +197,7 @@ export const CardInteractive = (props) => {
linkTitle,
linkLabel,
onClickCapture,
linkProps = {},
...rest
} = props;
const [isStateOver, setStateOver] = useState(false);
Expand All @@ -188,6 +216,7 @@ export const CardInteractive = (props) => {
<CardLink
to={linkTo}
title={linkTitle}
{...linkProps}
onMouseDown={() => setStateActive(true)}
onMouseUp={() => setStateActive(false)}
onMouseEnter={() => setStateOver(true)}
Expand All @@ -209,5 +238,6 @@ CardInteractive.propTypes = {
onClickCapture: T.func,
linkTo: T.string,
linkTitle: T.string,
linkLabel: T.string
linkLabel: T.string,
linkProps: T.object
};
Loading

0 comments on commit a59a9b8

Please sign in to comment.