Giter Club home page Giter Club logo

prism-app's Introduction

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:

    1. prism.json
    1. layers.json
    1. 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:

    1. legends.json
    1. 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 defining defaultDisplayBoundaries 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 as REACT_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 using REACT_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 and REACT_APP_OAUTH_REDIRECT_URI. Also, set the WFPAuthRequired 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.
Open http://localhost:3000 to view it in the browser.

The page will reload if you make edits.
You will also see any lint errors in the console.

yarn test

Launches the test runner in the interactive watch mode.
See the section about running tests for more information.

yarn build

Builds the app for production to the build folder.
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.
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.

prism-app's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

prism-app's Issues

Allow an nso (aka admin_level_data) layer to read data from a CSV file

The current implementation for nso layers was built around getting an API response as a json. More often, we're setting up these layers based on fairly static locally sourced datasets. Instead of requiring a json, allowing a CSV to provide the data and admin key would simplify steps to setup PRISM in new instances.

The ideal state is to have layer configuration work the same as current. The configuration in layers.json specifies the data field, admin key, and file location while the implementation detects if the file is a json or a CSV and renders the layer accordingly

Add tooltip on click

Popup with data details
When a user chooses a layer and then clicks on a polygon (administrative area), they should see a popup with details for the layer and that area. There are two types of data and requirements for this interaction:

  • Data already in the admin boundary GEOJSON, usually "baseline" data
  • Raster data, such as climate data derived from the Mongolia Data Cube (MDC)


Screen Shot 2020-05-05 at 2 42 50 PM

We can take inspiration from https://github.com/bayesimpact/encompass/tree/master/frontend/src/components/MapTooltip

More info is available here:
https://docs.google.com/document/d/1uD3S33TkKVUPRqkx2hQP65Nl6dOHBYenP1rroZHHbC4/edit

Display statistics

Through the PRISM interface, users are able to produce descriptive statistics interaction with administrative areas and indicator selection from drop-down menus

Host JSON data files outside of the code base

Right now JSON data files (e.g. admin boundaries, NSO files) are being directly imported into the code base (e.g. using an import statement in JS). This means that the full JSON data gets bundled into our javascript code, which is pretty seriously bloating the deployment.

These files should be hosted somewhere (e.g. S3 buckets) and fetched asynchronously instead.

Remove vertical scroll from main map container

Right now there's a slight vertical scroll for the main map container. This is because the map view is sized using 100vh, which doesn't account for the height of the navbar.

We should redo the structure of the page to make the the main wrapper a column-oriented flex container, with the navbar taking up its fixed height and the main container growing to fill the rest of the available space.

Send notification if Date Loading Fails

Currently, if dates fail to load from WMS, WCS or Point Data, an error is sent to the console. There used to be a notification, served through the error notification middleware, but this was removed because if an exception is thrown in serverUtils, all the other layers would not attempt loading dates.

To solve this, a notification could be dispatched instead of relying on the error middleware.
Not too much of a concern as date errors aren't likely to happen, but would be nice to solve eventually

https://github.com/WFP-VAM/prism-frontend/blob/5c68143ff2e15ac95127c8f5ceac673293d7096d/src/utils/server-utils.ts#L180

Config generator

Idea

Create a config generator to make it easier for new users to create or update their own configs.
We could also allow loading a file or using a remote config easily.

Allow loadLayerData to intelligently handle multiple requests for the same resource as efficiently as possible

This is a continuation of the concerns raised by @bencpeters at the following PR disscussion.

In summary, the way BoundaryLayers will load after #81 merges is less than ideal since it is done in MapView. We ideally want a way to load it in BoundaryLayer. If you have a solution, let us know!

Also, after exploring issue #27, I noticed loadLayerData does not have any way to prevent loading things twice or loading while the promise is pending. The current measurements that ensure this doesn't happen are located within the dispatch itself (example).

There is an article on Redux Docs on how to achieve this.

I've already attempted to solve this here, but the implementation isn't done too well - we mutate an object that just lives alongside the thunk. If we can agree this is a worthwhile issue, I can continue on this branch and make a solution that instead works around redux states.

Propose new UX for date selection

The current date selection implementation is not ideal. Let's propose improvements from a UX and UI perspective.

Current implementation

Screen Shot 2020-04-09 at 5 45 31 PM

Context
Layers can have data available daily, once per month or every 10 days. We can only select dated layer at a time.

Time-series choropleth map (area) from API

PRISM should have support to display a choropleth map (from statistics data in JSON format) with time-series variable. For example is to show the poverty rate in district level from year to year, or to support rendering "old version" of impact layers (the data also in statistics and JSON format).

PRISM already have the "NSO" layer type which can display choropleth map and also "point_data" which render selected time-series data from given link. I think the approach that we can use is NSO layer with capabilities to get and render data from API just like "point_data".

IDCO would like to prioritize in this feature as we need it to display socio-economic layer (ideally in time series so people can compare the changes from year to year) and support for "legacy" impact layer data. But first I would like to clarify and ask some question to @ericboucher and @dubwalla:

  • I would like to just confirm if the feature mentioned above is not already supported, or RBB team is currently developing a similar feature?

  • Should we create new category, or should we just expand from NSO layer type with more parameters something like: "timeseries": true, "data": http://linktoapi, etc.

  • Should we have more standardize API? Or should we make it more flexible, so user can setup the "GET" format in config file alongside with layer properties.

Thank you.

Dedicated property for text legend class

The current implementation of text legend class is to read the value properties. While now it can accommodate number and text, the way it implements somehow a bit confusing (I assume it will process integer/number first, then put all "value" including string for render the legend element.

What I would like to purpose is a dedicated "text" property for legend. Which can provide a more flexible and predictable way to generate text for legend, while separating the class for value classification.

"legend": [
{
"value": -40,
"color": "#7ab1c0",
"text": "-40° C"
},

First I would like to have input from @ericboucher about the idea. I would like to put the changes as part of understanding the ideal workflow of adding features into prism workflow.

Load WCS data

While we get WMS images as layers, we would also like to get raw data to run browser side analysis.

One possibility would be to get WCS layers as geotiff that we can then process in the frontend to analyze and merge.

Is this a reasonable approach? What other methods could we explore for browser-side analysis?

📦 Better use of ImmutableJS with Typescript

--> Comment on PR#32

We shouldn't mix and match Immutable & regular JS objects. Since this is all Redux state with Immutable, we'd like DateRange (and MapState for that matter) to be strongly typed Maps instead of objects or weak (because of the any) types.

This SO post is useful for that - read the first answer.

You can do something like:

// The default Map interface accepts <K,V>: Key, Value.
// Build an interface that also accepts 'T': the shape of your data.
// You might want to add other methods, e.g. `update`? Probably can cut and paste more or less from
// https://github.com/immutable-js/immutable-js/blob/7f4e61601d92fc874c99ccf7734d6f33239cec8c/type-definitions/Immutable.d.ts#L2449
export interface IImmutableMap<T, K, V> extends Map<K, V> {
  toJS(): T;
  get<I extends keyof T>(key: I & K): T[I] & V;
  set<S extends keyof T>(key: S & K, value: T[S] & V): Map<K, V>;
}


export interface IMapState extends Map<string, any> {
  layers: LayersMap,
  // Do a similar typed definition for `DateRange`
  dateRange: DateRange,
};

export type MapState = IImmutableMap<IMapState, string, any>;

// Update the type definition on initial state to your type.
const initialState: MapState = Map({
  // Not sure if you'll have to cast these or if TS will just pick them up as a compatible type
  layers: Map(),
  dateRange: Map(),
});

(haven't tested this, but something like it should work)

Add date as an attribute of ground station (aka point) and nso (aka admin_level) data layers

Each type of layer can have a date attribute associated with it but currently only WMS layers have this implemented. We need to be able to display the appropriate data based on a point in time. For example, for ground station data in Mongolia, a new reading is collected every 10 days but the current implementation can only show the latest available data. And in Indonesia, some of the baseline / vulnerability / nso layers may have different values based on the month and year.

An MVP version of this could include the layer configuration to specify the date interval required and available dates with data. A more ideal solution would to have some service that determines the interval and available dates automatically but that would require something like WMS GetCapabilities on external sources that we may not have control over. @ericboucher - what do you think?

A solution for this in Mongolia for the ground station data needs prioritization. A solution for nso layers could come later, but it would be great if we can solve for both cases together.

Improve Analysis UI

After #92 has been merged

It is the extension of the #86
This is in the continuation to New Analysis UI Footer
The new Analysis UI
New Analysis UI Footer

TODO

  • Add new footer UI
  • Improve clear analysis functionality
  • Improve Download functionality
  • Refactor the analysis layer
  • Improve the implementation of tooltip on the map

Allow the data field in an NSO data type to be configurable

Currently the NSO data type expects the data attribute to be mapped for an admin area to be called 'DTVAL_CO'. This was specific to Mongolia. To make deployments more efficient, we should allow this to be configurable for each layer, the same way that the admin code is configurable.

Make dates timezone insensitive

The DatePicker is timezone aware, which makes it tricky to save string dates into date formats and then get matching string dates again.

Eg. 2020-02-10 becoming 2020-02-11 in #25

To mitigate that, we should make the app timezone independent, esp. the DatePicker component.

package.json env definitions incompatible with Windows

Defining an environment variable like this ( "start": "EXTEND_ESLINT=true react-scripts start") makes it incompatible with Windows as EXTEND_ESLINT=true isn't a valid environment definition.

This can be solved by introducing cross-env. This will ensure the PRISM dev environment will work on any platform.

Make PRISM mobile friendly

Make the app more mobile-friendly or add modal notifying users that the app is not optimized for mobile yet.

Dev Server not working

Hey 👋
I am facing some issues in setting up a local environment.Since there isn't any Contribution guide, so I am not sure whether did I miss something or it's a bug somewhere (dependencies versions most probably)
I cloned the repo and install dependencies, without any configuration to the files I tried starting the dev server but I end up with some eslint plugin issue
image
Google wasn't a good help in this case, so maybe someone who already faced a similar issue might be able to help me.

List of commands I used:
git clone https://github.com/WFP-VAM/prism-frontend.git --depth=1
cd .\prism-frontend\
npm install
npm start

Error handling for layers

Handle errors when fetching data.

In particular, have the ability to parse time mismatch "Time dimension value '2020-02-10' not valid for this layer".

And server errors (eg. server offline)

New UI/Functionality for Legend Dropdown

The current Legend button:
image
Could use some improvement, notably in its style and functionality.

Ideas:

  1. The Hide/Show button could be styled to fit more into a Material UI theme, making it complement the switches.

  2. It could also be converted to a drop-down style, replacing the 'eye' with a chevron arrow.

  3. We could add crosses on every legend section so a user can quickly remove a layer by clicking the cross on the layer's legend.

Add a style attribute to WMS layers

WMS layers can have multiple styles associated with them but this isn't currently handled by the frontend. For example, WFP's Open Data Cube instance provides multiple styles for the same product which an end user can choose between based on regional differences or for different temporal aggregation. You can see this from the GetCapabilities request: https://odc.ovio.org/wms?request=GetCapabilities where the product 'rfh_dekad' has 5 different style options ('rfh_200' to 'rfh_1500').

Without the style attribute sent in the WMS request, ODC returns a default style, 'rfh_600' in this example. We need to be able to specify which style to load as part of the layer's configuration.

🚀 Avoid existing <Source> creation at each render

Related problems

  • With the async context initialization, the selected layers are re-render on server dates loaded.
  • When we add/remove a new layer, the previous layers get re-render (not super-efficient)

A first hacky solution (handling constant layers adding):

import React, { Fragment, useState, useEffect } from 'react';
import moment from 'moment';
import { Map } from 'immutable';
import { Source, Layer } from 'react-mapbox-gl';

import { formatServerUri } from '../../../utils/server-utils';
import { LayersMap, LayerType } from '../../../config/types';

const commonQueryParam = {
  version: '1.1.1',
  request: 'GetMap',
  format: 'image/png',
  transparent: true,
  exceptions: 'application/vnd.ogc.se_inimage',
  bboxsr: 3857,
  imagesr: 3857,
  width: 256,
  height: 256,
  srs: 'EPSG:3857',
  bbox: '{bbox-epsg-3857}',
};

function Layers({ layers = Map(), selectedDate }: LayersProps) {
  const [sources, SetSources] = useState(Map<string, any>());

  const queryParam = {
    ...commonQueryParam,
    ...(selectedDate && {
      time: moment(selectedDate).format('YYYY-MM-DD'),
    }),
  };

  useEffect(() => {
    const lastLayer = layers.last() as LayerType;
    if (lastLayer) {
      const { id, serverUri } = lastLayer;
      SetSources(
        sources.set(
          id,
          <Source
            id={`source-${id}`}
            tileJsonSource={{
              type: 'raster',
              tiles: serverUri && [formatServerUri(serverUri, queryParam)],
              tileSize: 256,
            }}
          />,
        ),
      );
    }
  }, [layers]);

  return (
    <>
      {layers
        .valueSeq()
        .toJS()
        .map(({ id, opacity }) => (
          <Fragment key={id}>
            {sources.get(id) ? (
              <>
                {sources.get(id)}{' '}
                <Layer
                  type="raster"
                  id={`layer-${id}`}
                  sourceId={`source-${id}`}
                  paint={{ 'raster-opacity': opacity }}
                />
              </>
            ) : null}
          </Fragment>
        ))}
    </>
  );
}

export interface LayersProps {
  layers: LayersMap;
  selectedDate?: number;
}

export default Layers;

Create a separate UI for invoking an analysis

With the addition of new API requests to fetch an analysis output, we need a new UX / UI which is differentiated from the standard layer views. A user needs to:

  1. Configure an analysis
  2. Run the analysis
  3. Receive an indication of the analysis job's status
  4. View the results as a map or table
  5. Download the results

Some rough ideas below but please suggest options / alternatives.

Separate UI element to run an analysis
Screenshot 2020-06-19 18 16 26

Analysis pane activated:
Screenshot 2020-06-19 18 18 00

After staring an analysis job, some user awareness of status:
Screenshot 2020-06-19 18 18 59

Buttons active when the analysis job is complete. User can view or download the results:
Screenshot 2020-06-19 18 19 53

View the results as a map:
Screenshot 2020-06-19 18 21 11

View the results as a table:
Screenshot 2020-06-19 18 21 41

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.