Awesome
WFP PRISM Frontend
This project is the front-end interface for the World Food Programme's PRISM project. It displays data and impact projections on a configurable map interface.
Functionalities
The new PRISM frontend is built as a static website to minimize cross dependencies and simplify deployments as much as possible. Currently, PRISM frontend provides the ability to:
- Load administrative boundaries as GeoJSON (
src/config/admin_boundaries.json
) - Load admin level (vector) data as JSON, and link it to administrative boundaries
- Display WMS layers from Geoserver or Open Data Cube endpoints, with date selection capabilities
- Display point layers by applying symbology to numeric values associated with a geographic coordinate
- Display CSV tables in a left side panel
To chose which country to run PRISM for, you can set the environment variable REACT_APP_COUNTRY
. The current default country is mozambique
Configuration
The configuration is split into three files that you can find in src/config
:
-
prism.json
-
layers.json
-
tables.json
Since many layers are common across multiple countries, we created shared configuration files that any deployment can access. These layers are generated by WFP globally and made available through the Humanitarian Data Cube. You can find the layers and their associated styles / legends in src/config/shared
:
-
legends.json
-
layers.json
prism.json
This is the primary configuration file. You can define:
- Map settings (starting point, zoom, default boundary layers)
- The server endpoints
- Categories and their respective icons which are used to organize the top navigation
- Alerts flag (to specify whether to activate the alerts module)
For each category, you can define sub categories as "subcategorie_name":
[layers], a list of layers from layers.json
.
{
"country": "Cambodia",
"map": {
"latitude": 12.058,
"longitude": 105.281,
"zoom": 6.49
},
"serversUrls": {
"wms": [
"https://geonode.wfp.org/geoserver/prism/wms/",
"https://ows.earthobservation.vam.wfp.org/wms"
]
},
"alertFormActive": false,
"hidePanel": false,
"icons": {
"vulnerability": "icon_vulnerable.png",
"exposure": "icon_basemap.png",
"hazards": "icon_climate.png",
"risk": "icon_impact.png",
"capacity": "icon_capacity.png",
"tables": "icon_table.png"
},
"categories": {
"hazards": {
"floods" ....
Map settings
For each country, you will need to specify the starting point (lat, long) and zoom. In addition, you can also set
- maxBounds
- minZoom
- maxZoom
To find these attributes, we created a help mode that you can activate by setting the env REACT_APP_SHOW_MAP_INFO=true
Boundary layers
-
Configuring multiple boundary layers If multiple boundary layers are configured in
layers.json
you can specify which should be displayed by default by definingdefaultDisplayBoundaries
as an array of boundaries.e.g.
{ ... "defaultDisplayBoundaries": [ "township_boundaries", "district_boundaries", "state_boundaries" ] ... }
In some cases, a boundary file may load without any issues but fail to provide correct results in an exposure analysis, or correctly render the 'Go To' feature. Troubleshooting this is challenging. It is recommended to use a validation tool on your boundary file before utilizing it in PRISM. https://geojsonlint.com/ works well for this purpose.
In addition, boundary files sometimes carry more precise coordinates than is neccessary which makes for a large data file. PRISM will alert you with a message in the browser if the precision is too high. You can run bash /frontend/scripts/truncate_precision.sh to fix this. The script will update any boundary file in the /frontend/public/data folder
Data Layers
Under categories
, you can specify the menu structure and reference to any layer by ID that is defined under the country country specific or shared layers.json
. You can also create groups that can be activated all at once, or selectively.
"categories": {
"rainfall": {
"forecasts": ["daily_rainfall_forecast", "dekad_rainfall_forecast"],
"rainfall_amount": [
{
"group_title": "Rainfall Aggregate",
"activate_all": false,
"layers": [
{
"id": "rainfall_dekad",
"label": "10-day",
"main": true
},
{
"id": "rainfall_agg_1month",
"label": "1-month"
},
{
"id": "rainfall_agg_3month",
"label": "3-month"
},
{
"id": "rainfall_agg_6month",
"label": "6-month"
},
{
"id": "rainfall_agg_9month",
"label": "9-month"
},
{
"id": "rainfall_agg_1year",
"label": "1-year"
}
]
}
],
}
}
layers.json
In this file, we define the specific layer settings for data access, titles, and legends. You can define a new layer from scratch, or override a layer that exists in the shared/layers.json
. In that case, you only need to specify the fields that need to be overriden. Similarly, legends can be predefined in shared/legends.json
and simply reference to by id. The "legend_text"
attribute describes the source of the data and can use Markdown syntax to render links, italics, and bold text within our H5 typography.
There are 4 main types of layers.
raster
These layers are simply processed as raster images from a WMS server and are referred to as type 'wms'
"pasture_anomaly": {
"title": "Pasture anomaly",
"server_type": "wms",
"server_layer": "ModisAnomaly",
"server_uri": "https://mongolia.sibelius-datacube.org:5000/wms?layers=ModisAnomaly",
"has_date": true,
"date_interval": "days",
"opacity": 0.3,
"legend_text": "Converts NDVI to pasture (kg/ha) values and divides the current period by the long term average to calculate pasture anomaly.",
"legend": [
{ "value": "12000", "color": "#ff0000" },
{ "value": "14000", "color": "#ff5900" },
{ "value": "16000", "color": "#ff8400" },
{ "value": "18000", "color": "#ffce63" },
{ "value": "19000", "color": "#ffdd94" },
{ "value": "20000", "color": "#ffffbf" },
{ "value": "21000", "color": "#dee09f" },
{ "value": "22000", "color": "#bbbf7c" },
{ "value": "24000", "color": "#9da360" },
{ "value": "26000", "color": "#7e8745" },
{ "value": "28000", "color": "#5b6e00" }
]
}
admin level
These layers are referred to as admin_level_data
in PRISM and represent a data value for a polygon. The layers are obtained by matching data from the data_field
and admin_code
fields of the admin_level_data
layer with the administrative boundaries. The default admin boundary file will be used unless otherwise specifed in the admin_level_data
configuration using the boundary
attribute
"improved_drinking_water": {
"title": "Improved drinking water",
"type": "admin_level_data",
"path": "data/myanmar/nso/vulnerability-layers.json",
"data_field": "improved_drinking_water",
"admin_level": 3,
"admin_code": "TS_PCODE",
"opacity": 0.7,
"legend": [
{ "label": "<20%", "value": 0, "color": "#a50f15" },
{ "label": "21 to 40%", "value": 21, "color": "#de2d26" },
{ "label": "41 to 60%", "value": 41, "color": "#fb6a4a" },
{ "label": "61 to 80%", "value": 61, "color": "#fcae91" },
{ "label": "81 to 100%", "value": 81, "color": "#fee5d9" }
],
"legend_text": "Percent of households with improved source of drinking water. Source: Myanmar Population and Housing Census 2014, Department of Population, Ministry of Immigration and Population"
}
point
These layers are referred to as point_data
in PRISM and represent a data value for a given latitude and longitude coordinate. Point data layers visualize values specified as measure_field
as points on a map based on the geom_field
which expect a lat, long coordinate.
"disaster_report": {
"title": "Disaster impact report",
"type": "point_data",
"data": "https://prism-api.ovio.org/kobo/forms",
"additional_query_params": {
"form_name": "PRISM-KHM-Disaster-Report-v1",
"datetime_field": "disaster_date",
"geom_field": "location",
"measure_field": "num_ppl_affected",
}
"opacity": 0.9,
"legend_text": "Number of people affected",
"legend": [
{"value": "0", "color": "#909090"},
{"value": "< 100", "color": "#ffeda0"},
{"value": "100 - 500", "color": "#feb24c"},
{"value": "500 or more", "color": "#f03b20"}
]
boundaries
Boundary layers are loaded by defaul when the application starts and typically show administrative boundaries and are defined as type boundary
. Multiple boundary files can be configured in layers.json. Multiple boundary files can be used to create different styles for each boundary, or to toggle between admin_level_data layers which correspond to a separate geographic specification; for example to use one boundary file for district level data, and another boundary file for ecological data.
When more than one boundary is specified, an array of boundaries needs to also be set in prism.json
using with the defaultDisplayBoundaries
attribute.
{
"state_admin_boundaries": {
"type": "boundary",
"path": "data/myanmar/mmr_admin1_boundaries.json",
"opacity": 0.8,
"admin_code": "ST_PCODE",
"admin_level_names": ["ST"],
"admin_level_local_names": ["mmr_polbnd"],
"styles:": {
"fill": {
"fill-opacity": 0
},
"line": {
"line-color": "gray",
"line-width": 1.5,
"line-opacity": 0.8
}
}
},
"district_admin_boundaries": {
"type": "boundary",
"path": "data/myanmar/mmr_admin2_boundaries.json",
"opacity": 0.8,
"admin_code": "DT_PCODE",
"admin_level_names": ["ST", "DT"],
"admin_level_local_names": ["DT_MMR4", "TS_MMR4"],
"styles:": {
"fill": {
"fill-opacity": 0
},
"line": {
"line-color": "gray",
"line-width": 1,
"line-opacity": 0.8
}
}
},
"admin_boundaries": {
"type": "boundary",
"path": "data/myanmar/admin_boundaries.json",
"opacity": 0.8,
"admin_code": "TS_PCODE",
"admin_level_names": ["ST", "DT", "TS"],
"admin_level_local_names": ["mmr_polbnd", "DT_MMR4", "TS_MMR4"],
"styles:": {
"fill": {
"fill-opacity": 0
},
"line": {
"line-color": "gray",
"line-width": 0.5,
"line-opacity": 0.8
}
}
}
impact
Impact layers are computed by combining a raster layer with a vector layer based on raster values bound by the zones of the vector layer. The impact layer computes zonal statistics for the raster, and based on a configured threshold, will display zones where the threshold has been exceeded.
"herd_pasture_impact": {
"title": "Number of herder households exposed to severe pasture anomaly",
"hazard_layer": "pasture_anomaly",
"baseline_layer": "nsoHerders",
"threshold": " <= 25000",
"opacity": 0.3,
"legend_text": "Number of herder households within ADMIN2 in an area where the median pasture anomaly is <= -50%",
"legend": [
{ "value": "25", "color": "#ffeda0”" },
{ "value": "30", "color": "#feb24c”" },
{ "value": "35", "color": "#f03b20”" }
]
}
Additional layer content
Add Layer Contents
To display additional metadata about a layer, you can add a content_path
attribute to any layer. The attribute expects a path to a .md
or .html
file that is stored in public/data/${REACT_APP_COUNTRY}/filename.ext
directory. For example: public/data/myanmar/contents.md
The application will show an icon next to the layer in the legend if this attribute is configured, and will display the content in a modal window if the icon is clicked.
Technical - Packages/Dependencies
This project was bootstrapped with Create React App, using the Redux and Redux Toolkit template with TypeScript.
- Styling & UI Library Use Material UI. Note that to use the styles API you can
import @material-ui/core/styles
. - Routing Uses React Router.
- Monitoring Uses Sentry.io. To send monitoring info to Sentry, simply set the
Sentry
url by adding it asREACT_APP_SENTRY_URL
in a.env
file at the root folder. - State Management Uses Redux
- Testing Uses Jest with Enzyme
- Mapping Uses MapLibre. The app supports Maptiler and Mapbox styles. To use Mapbox styles, you will need to create a token and add it as
REACT_APP_MAPBOX_TOKEN
in a.env
file at the root folder. Then specify your style url usingREACT_APP_DEFAULT_STYLE
. - WFP authentication Uses msal. You need to include within your .env file the variables
REACT_APP_OAUTH_CLIENT_ID
,REACT_APP_OAUTH_AUTHORITY
andREACT_APP_OAUTH_REDIRECT_URI
. Also, set theWFPAuthRequired
flag within the country prism.json file
Developing the frontend
The following commands should get you a local development instance of the frontend:
cd frontend
yarn clean
yarn install
yarn setup:common
REACT_APP_COUNTRY=cambodia yarn start
Available Scripts
In the project directory, you can run:
yarn start
Runs the app in the development mode.<br /> Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.<br /> You will also see any lint errors in the console.
yarn test
Launches the test runner in the interactive watch mode.<br /> See the section about running tests for more information.
yarn build
Builds the app for production to the build
folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br /> Your app is ready to be deployed!
See the section about deployment for more information.
yarn lint
Runs eslint
for all the source files. We use a custom Eslint configuration in ./eslintrc
along with prettier
(configured with ./.prettierrc
) to enforce consistency and code quality. If you would like eslint to try to automatically "fix" files if it can, run yarn lint --fix
.
Committing Code
By default, a pre-commit hook is defined to run linting tasks on all staged code before allowing a commit. This occurs using the lint-staged package, and can be configured in ./package.json#lint-staged
. The precommit task can be run manually using yarn precommit
.
Creating pull request and deploying on Surge
By default, everytime a pull request is created, a CI/CD pipeline will run tests and deploy the code on a surge page (http://prism-[pr number].surge.sh). To specify a country that the build will be run on, start your pull request title with COUNTRY=[country name]
. For example: COUNTRY=cambodia Add new config options
.