@nsidc/snow-today-webapp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +112 -0
- package/.dockerignore +2 -0
- package/.eslintrc.js +43 -0
- package/.stylelintrc.json +6 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/Dockerfile +40 -0
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/doc/TODO.md +14 -0
- package/doc/_images/snow-today-screenshot.png +0 -0
- package/doc/architecture.md +24 -0
- package/doc/development.md +34 -0
- package/docker-compose.dev.yml +21 -0
- package/docker-compose.live.yml +8 -0
- package/docker-compose.yml +21 -0
- package/index.html +13 -0
- package/jest/README.md +8 -0
- package/jest/scripts/setupTests.ts +5 -0
- package/jest/scripts/test.js +19 -0
- package/jest/transforms/babelTransform.js +29 -0
- package/jest/transforms/cssTransform.js +14 -0
- package/jest/transforms/fileTransform.js +42 -0
- package/nginx/default.conf +16 -0
- package/nginx/default.dataserver.conf +25 -0
- package/nginx/nginx.conf +33 -0
- package/nginx/openssl.conf +21 -0
- package/package.json +125 -0
- package/postcss.config.js +5 -0
- package/public/index.html +18 -0
- package/public/manifest.json +9 -0
- package/src/App.tsx +24 -0
- package/src/clientState/rasterOpacity/atom.ts +9 -0
- package/src/clientState/rasterOpacity/index.ts +3 -0
- package/src/clientState/selectedBasemap/atom.ts +10 -0
- package/src/clientState/selectedBasemap/index.ts +3 -0
- package/src/clientState/selectedBasemapObject.ts +21 -0
- package/src/clientState/selectedRegion/atom.ts +9 -0
- package/src/clientState/selectedRegion/index.ts +3 -0
- package/src/clientState/selectedRegionObject.ts +29 -0
- package/src/clientState/selectedSatelliteVariable/atom.ts +9 -0
- package/src/clientState/selectedSatelliteVariable/index.ts +3 -0
- package/src/clientState/selectedSatelliteVariableObject.ts +30 -0
- package/src/components/ControlPanel/BasemapSelector.tsx +26 -0
- package/src/components/ControlPanel/DebugDumpButton.tsx +21 -0
- package/src/components/ControlPanel/RasterOpacitySlider.tsx +24 -0
- package/src/components/ControlPanel/RegionSelector.tsx +62 -0
- package/src/components/ControlPanel/SatelliteVariableSelector.tsx +59 -0
- package/src/components/ControlPanel/index.tsx +32 -0
- package/src/components/LinePlot.tsx +142 -0
- package/src/components/SlippyMap.tsx +100 -0
- package/src/components/__tests__/BasemapSelector.tsx +25 -0
- package/src/constants/dataServer.ts +9 -0
- package/src/index.tsx +43 -0
- package/src/serverState/plotData.ts +28 -0
- package/src/serverState/regionShape.ts +22 -0
- package/src/serverState/regionsIndex.ts +26 -0
- package/src/serverState/variablesIndex.ts +26 -0
- package/src/style/App.css +25 -0
- package/src/style/BasemapSelector.css +7 -0
- package/src/style/ControlPanel.css +9 -0
- package/src/style/DebugDumpButton.css +9 -0
- package/src/style/LinePlot.css +9 -0
- package/src/style/RegionSelector.css +7 -0
- package/src/style/SlippyMap.css +19 -0
- package/src/style/VariableSelector.css +7 -0
- package/src/style/card.css +15 -0
- package/src/style/index.css +13 -0
- package/src/types/Basemap.ts +19 -0
- package/src/types/SlippyMap.ts +16 -0
- package/src/types/misc.ts +3 -0
- package/src/types/query/plotData.ts +9 -0
- package/src/types/query/regions.ts +11 -0
- package/src/types/query/satelliteVariables.ts +15 -0
- package/src/util/__tests__/colormap.ts +28 -0
- package/src/util/colormap.ts +48 -0
- package/src/util/fetch/plotData.ts +20 -0
- package/src/util/fetch/regions.ts +30 -0
- package/src/util/fetch/variables.ts +17 -0
- package/src/util/layer/basemaps.ts +118 -0
- package/src/util/layer/raster.ts +97 -0
- package/src/util/layer/regionShape.ts +45 -0
- package/src/util/layer/source.ts +28 -0
- package/src/util/layer/switch.ts +65 -0
- package/src/util/query.ts +4 -0
- package/src/util/sideEffects/slippyMap.ts +123 -0
- package/src/util/test.ts +13 -0
- package/tsconfig.json +31 -0
- package/webpack.config.js +76 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
setup: &setup
|
|
4
|
+
working_directory: ~/snow-today-webapp
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
test-and-bundle:
|
|
8
|
+
<<: *setup
|
|
9
|
+
docker:
|
|
10
|
+
- image: cimg/node:18.7
|
|
11
|
+
steps:
|
|
12
|
+
- checkout
|
|
13
|
+
- run:
|
|
14
|
+
name: Install dependencies
|
|
15
|
+
command: npm ci
|
|
16
|
+
- run:
|
|
17
|
+
name: Lint
|
|
18
|
+
command: npm run lint
|
|
19
|
+
- run:
|
|
20
|
+
name: Test
|
|
21
|
+
command: npm run test
|
|
22
|
+
- run:
|
|
23
|
+
name: Build Bundle
|
|
24
|
+
command: npm run build
|
|
25
|
+
- persist_to_workspace:
|
|
26
|
+
root: ~/snow-today-webapp
|
|
27
|
+
paths: .
|
|
28
|
+
|
|
29
|
+
publish-bundle-to-npm:
|
|
30
|
+
<<: *setup
|
|
31
|
+
docker:
|
|
32
|
+
- image: cimg/node:18.7
|
|
33
|
+
steps:
|
|
34
|
+
- attach_workspace:
|
|
35
|
+
at: ~/snow-today-webapp
|
|
36
|
+
- run:
|
|
37
|
+
name: Publish NPM Package
|
|
38
|
+
command: |
|
|
39
|
+
echo "//registry.npmjs.org/:_authToken=$NPM_DEPLOY_TOKEN" > .npmrc
|
|
40
|
+
npm publish --access public
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
build-docker-image-and-sometimes-publish:
|
|
44
|
+
<<: *setup
|
|
45
|
+
docker:
|
|
46
|
+
- image: docker:20.10.3-git
|
|
47
|
+
steps:
|
|
48
|
+
- checkout
|
|
49
|
+
# `setup_remote_docker` defaults to 17.09.0 which doesn't work with the
|
|
50
|
+
# node image we're trying to build...
|
|
51
|
+
# https://support.circleci.com/hc/en-us/articles/360050934711
|
|
52
|
+
# https://discuss.circleci.com/t/docker-build-fails-with-nonsensical-eperm-operation-not-permitted-copyfile/37364
|
|
53
|
+
- setup_remote_docker:
|
|
54
|
+
version: 20.10.2
|
|
55
|
+
- run:
|
|
56
|
+
name: Build and push Docker image
|
|
57
|
+
command: |
|
|
58
|
+
IMAGE_NAME="nsidc/snow-today-webapp"
|
|
59
|
+
|
|
60
|
+
echo "\$CIRCLE_TAG: ${CIRCLE_TAG}"
|
|
61
|
+
echo "\$CIRCLE_BRANCH: ${CIRCLE_BRANCH}"
|
|
62
|
+
|
|
63
|
+
if [[ "${CIRCLE_TAG}" ]]; then
|
|
64
|
+
TAG=${CIRCLE_TAG}
|
|
65
|
+
elif [[ "${CIRCLE_BRANCH}" = "main" ]]; then
|
|
66
|
+
TAG="latest"
|
|
67
|
+
else
|
|
68
|
+
# We don't really want images named after tags cluttering up our
|
|
69
|
+
# DH repo, so we use workflow filters to prevent this job from
|
|
70
|
+
# being triggered on a branch. Change the filters if we change
|
|
71
|
+
# our mind.
|
|
72
|
+
TAG=${CIRCLE_BRANCH}
|
|
73
|
+
fi
|
|
74
|
+
echo "\$TAG: ${TAG}"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
DOCKER_IMAGE="${IMAGE_NAME}:${TAG}"
|
|
78
|
+
docker build -t ${DOCKER_IMAGE} .
|
|
79
|
+
echo "Built: ${DOCKER_IMAGE}"
|
|
80
|
+
|
|
81
|
+
docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
|
|
82
|
+
docker push "${DOCKER_IMAGE}"
|
|
83
|
+
|
|
84
|
+
workflows:
|
|
85
|
+
version: 2
|
|
86
|
+
|
|
87
|
+
build:
|
|
88
|
+
jobs:
|
|
89
|
+
- test-and-bundle:
|
|
90
|
+
filters:
|
|
91
|
+
tags:
|
|
92
|
+
only: /.*/
|
|
93
|
+
|
|
94
|
+
- publish-bundle-to-npm:
|
|
95
|
+
context: org-global
|
|
96
|
+
requires:
|
|
97
|
+
- test-and-bundle
|
|
98
|
+
filters:
|
|
99
|
+
branches:
|
|
100
|
+
ignore: /.*/
|
|
101
|
+
tags:
|
|
102
|
+
only: /^v\d+\.\d+\.\d+([\-\.\w]*)?$/
|
|
103
|
+
|
|
104
|
+
- build-docker-image-and-sometimes-publish:
|
|
105
|
+
context: org-global
|
|
106
|
+
requires:
|
|
107
|
+
- test-and-bundle
|
|
108
|
+
filters:
|
|
109
|
+
branches:
|
|
110
|
+
only: main
|
|
111
|
+
tags:
|
|
112
|
+
only: /^v[0-9]+(\.[0-9]+)*(\.[\-a-zA-Z0-9]+)?$/
|
package/.dockerignore
ADDED
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
"parser": "@typescript-eslint/parser",
|
|
3
|
+
// The below config enables type-aware linting, but significantly slows down
|
|
4
|
+
// eslint.
|
|
5
|
+
// https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/README.md#configuration
|
|
6
|
+
"parserOptions": {
|
|
7
|
+
"tsconfigRootDir": __dirname,
|
|
8
|
+
"project": ["./tsconfig.json"],
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
"@typescript-eslint",
|
|
12
|
+
"react-hooks",
|
|
13
|
+
],
|
|
14
|
+
"extends": [
|
|
15
|
+
"react-app",
|
|
16
|
+
"react-app/jest",
|
|
17
|
+
"plugin:@typescript-eslint/recommended",
|
|
18
|
+
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
|
19
|
+
],
|
|
20
|
+
"rules": {
|
|
21
|
+
"eqeqeq": [
|
|
22
|
+
"error",
|
|
23
|
+
],
|
|
24
|
+
"react/function-component-definition": [
|
|
25
|
+
"error",
|
|
26
|
+
{
|
|
27
|
+
"namedComponents": "arrow-function",
|
|
28
|
+
"unnamedComponents": "arrow-function",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
"react-hooks/rules-of-hooks": "error",
|
|
32
|
+
"react-hooks/exhaustive-deps": "warn",
|
|
33
|
+
"@typescript-eslint/ban-ts-comment": [
|
|
34
|
+
"warn",
|
|
35
|
+
{
|
|
36
|
+
"ts-ignore": "allow-with-description",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
"@typescript-eslint/no-explicit-any": [
|
|
40
|
+
"warn",
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
|
8
|
+
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
9
|
+
level of experience, education, socio-economic status, nationality, personal
|
|
10
|
+
appearance, race, religion, or sexual identity and orientation.
|
|
11
|
+
|
|
12
|
+
## Our Standards
|
|
13
|
+
|
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
|
15
|
+
include:
|
|
16
|
+
|
|
17
|
+
* Using welcoming and inclusive language
|
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
|
19
|
+
* Gracefully accepting constructive criticism
|
|
20
|
+
* Focusing on what is best for the community
|
|
21
|
+
* Showing empathy towards other community members
|
|
22
|
+
|
|
23
|
+
Examples of unacceptable behavior by participants include:
|
|
24
|
+
|
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
26
|
+
advances
|
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
28
|
+
* Public or private harassment
|
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
|
30
|
+
address, without explicit permission
|
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
32
|
+
professional setting
|
|
33
|
+
|
|
34
|
+
## Our Responsibilities
|
|
35
|
+
|
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
|
38
|
+
response to any instances of unacceptable behavior.
|
|
39
|
+
|
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
|
44
|
+
threatening, offensive, or harmful.
|
|
45
|
+
|
|
46
|
+
## Scope
|
|
47
|
+
|
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
|
49
|
+
when an individual is representing the project or its community. Examples of
|
|
50
|
+
representing a project or community include using an official project e-mail
|
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
|
53
|
+
further defined and clarified by project maintainers.
|
|
54
|
+
|
|
55
|
+
## Enforcement
|
|
56
|
+
|
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
58
|
+
reported by contacting the project team at nsidc@nsidc.org. All
|
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
|
63
|
+
|
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
|
66
|
+
members of the project's leadership.
|
|
67
|
+
|
|
68
|
+
## Attribution
|
|
69
|
+
|
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
71
|
+
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
72
|
+
|
|
73
|
+
[homepage]: https://www.contributor-covenant.org
|
|
74
|
+
|
|
75
|
+
For answers to common questions about this code of conduct, see
|
|
76
|
+
https://www.contributor-covenant.org/faq
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
FROM node:18-alpine AS builder
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
COPY package* ./
|
|
6
|
+
# Install from lockfile
|
|
7
|
+
RUN npm ci
|
|
8
|
+
|
|
9
|
+
# NOTE: _DO NOT_ copy node_modules, that would overwrite the install we just
|
|
10
|
+
# did.
|
|
11
|
+
# COPY scripts ./scripts
|
|
12
|
+
COPY src ./src
|
|
13
|
+
COPY index.html ./
|
|
14
|
+
COPY webpack.config.js tsconfig.json .eslintrc.js .stylelintrc.json ./
|
|
15
|
+
|
|
16
|
+
ARG env="production"
|
|
17
|
+
RUN if [ "$env" = "production" ]; then \
|
|
18
|
+
npm run build; \
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# These lines are only required to use this stage of the build to run a dev
|
|
22
|
+
# server (see docker-compose.dev.yml).
|
|
23
|
+
EXPOSE 3000
|
|
24
|
+
CMD ["npm", "run", "serve"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
FROM nginx:1.23-alpine AS server
|
|
28
|
+
|
|
29
|
+
WORKDIR /usr/share/nginx/html
|
|
30
|
+
COPY --from=builder /app/dist .
|
|
31
|
+
|
|
32
|
+
# Make a self-signed SSL certificate
|
|
33
|
+
RUN mkdir /cert
|
|
34
|
+
COPY ./nginx/openssl.conf .
|
|
35
|
+
RUN apk add openssl
|
|
36
|
+
RUN openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -config openssl.conf \
|
|
37
|
+
-keyout /cert/ssl.key -out /cert/ssl.crt
|
|
38
|
+
|
|
39
|
+
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
|
|
40
|
+
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Regents of the University of Colorado
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice (if present) and this permission notice shall be
|
|
13
|
+
included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Snow Today Webapp
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Enable users to browse daily snow variables in various regions (South-West US, maybe
|
|
6
|
+
eventually High Mountain Asia).
|
|
7
|
+
|
|
8
|
+
Depends on [Snow Today Webapp Server](https://github.com/nsidc/snow-today-webapp-server)
|
|
9
|
+
to function.
|
|
10
|
+
|
|
11
|
+
**WARNING: This software is currently in early development. The data required for the
|
|
12
|
+
server component is not yet publicly exposed, so you should not expect this to work on
|
|
13
|
+
your machine.**
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Level of Support
|
|
17
|
+
|
|
18
|
+
This repository is not actively supported by NSIDC but we welcome issue submissions and
|
|
19
|
+
pull requests in order to foster community contribution.
|
|
20
|
+
|
|
21
|
+
See the [LICENSE](LICENSE) for details on permissions and warranties. Please contact
|
|
22
|
+
nsidc@nsidc.org for more information.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
This package requires either Docker or the NPM environment described by `package.json`.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
docker-compose build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
docker-compose up
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Troubleshooting
|
|
45
|
+
|
|
46
|
+
*TODO*
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
See [LICENSE](LICENSE).
|
|
52
|
+
|
|
53
|
+
## Code of Conduct
|
|
54
|
+
|
|
55
|
+
See [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
56
|
+
|
|
57
|
+
## Credit
|
|
58
|
+
|
|
59
|
+
This software was developed by the National Snow and Ice Data Center with funding from
|
|
60
|
+
multiple sources.
|
package/doc/TODO.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# TODO
|
|
2
|
+
|
|
3
|
+
## Names
|
|
4
|
+
|
|
5
|
+
* RasterVariable -> SatelliteDataVariable ?
|
|
6
|
+
|
|
7
|
+
## Versioning
|
|
8
|
+
|
|
9
|
+
* Version this app!
|
|
10
|
+
* CHANGELOG
|
|
11
|
+
* How to keep track of compatibility between the server (data schemas) and this app
|
|
12
|
+
(query types)? Can we write a utility that analyzes jsonschema files with typescript
|
|
13
|
+
type definitions for compatibility? Maybe we could use `typescript-json-schema` to
|
|
14
|
+
convert the Typescript interfaces to JSON schemas and diff?
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
This application has a client which runs in a user's browser that is responsible for
|
|
4
|
+
fetching data and visualizing it, and a server which is responsible for serving up
|
|
5
|
+
static data files generated in combination by code in this repository and external code
|
|
6
|
+
running on external systems.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Client
|
|
10
|
+
|
|
11
|
+
This repository contains a Typescript codebase for visualizing various data stored on a
|
|
12
|
+
server.
|
|
13
|
+
|
|
14
|
+
The client uses OpenLayers to display geospatial data, and ??? to display plots. It uses
|
|
15
|
+
`react-query` to manage many communications with the server, but for fetching
|
|
16
|
+
Cloud-Optimized GeoTIFFs, OpenLayers directly communicates with the server.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Server
|
|
20
|
+
|
|
21
|
+
The server is required for the client to function. The server is a simple HTTP server,
|
|
22
|
+
e.g. NGINX or Apache, serving static data files which must be structured in a specific
|
|
23
|
+
layout. See the [Snow Today Webapp Server
|
|
24
|
+
repository](https://github.com/nsidc/snow-today-webapp-server) for details.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Development
|
|
2
|
+
|
|
3
|
+
To use the development configuration, create a compose override file:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
ln -s docker-compose.dev.yml docker-compose.override.yml
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## State management
|
|
11
|
+
|
|
12
|
+
This app uses Recoil for state management.
|
|
13
|
+
|
|
14
|
+
Recoil freezes all objects used for state, so do not use objects that are expected to be
|
|
15
|
+
mutated in state, e.g. Layer objects. They are OK in derived state (with
|
|
16
|
+
`allowDangerousMutations`), but not state that will be directly updated.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Cloud-Optimized GeoTIFFs
|
|
20
|
+
|
|
21
|
+
The raster data is stored in COGs accessed by HTTP.
|
|
22
|
+
|
|
23
|
+
The COGs _must_ be in the same projection as the OpenLayers map in order to properly
|
|
24
|
+
display. A script `scripts/make_cogs.sh` can prepare the files correctly.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## TODO
|
|
28
|
+
|
|
29
|
+
### GeoTIFF Hosting
|
|
30
|
+
|
|
31
|
+
How are we going to deploy the server hosting the GeoTIFFs?
|
|
32
|
+
|
|
33
|
+
What's the simplest way to access those images in dev? Should we break that part into a
|
|
34
|
+
different repo?
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
version: '3.4'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
webapp:
|
|
5
|
+
build:
|
|
6
|
+
context: "."
|
|
7
|
+
target: "builder"
|
|
8
|
+
args:
|
|
9
|
+
- "env=dev"
|
|
10
|
+
image: "nsidc/snow-today-webapp:dev"
|
|
11
|
+
restart: "on-failure"
|
|
12
|
+
# Useful to skip linting errors and expose compiler errors:
|
|
13
|
+
# command: 'npm run serve:nolint'
|
|
14
|
+
ports:
|
|
15
|
+
# Webpack dev server and websocket for hot reload:
|
|
16
|
+
- "8080:8080"
|
|
17
|
+
volumes:
|
|
18
|
+
- "./src:/app/src:ro"
|
|
19
|
+
- "./index.html:/app/index.html:ro"
|
|
20
|
+
- "./public:/app/public:ro"
|
|
21
|
+
- "./.eslintrc.js:/app/.eslintrc.js:ro"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# WARNING: This compose file alone will not yield a working stack. It is
|
|
2
|
+
# intended for use with an override (included).
|
|
3
|
+
version: '3.4'
|
|
4
|
+
|
|
5
|
+
services:
|
|
6
|
+
webapp:
|
|
7
|
+
image: "nsidc/snow-today-webapp:${APP_VERSION:-latest}"
|
|
8
|
+
# TODO: Pull, don't build in non-dev
|
|
9
|
+
build: .
|
|
10
|
+
logging:
|
|
11
|
+
options:
|
|
12
|
+
max-size: "10m"
|
|
13
|
+
max-file: "10"
|
|
14
|
+
|
|
15
|
+
data-server:
|
|
16
|
+
image: "nginx:1.23"
|
|
17
|
+
volumes:
|
|
18
|
+
- "/share/apps/snow-today:/usr/share/nginx/html:ro"
|
|
19
|
+
- "./nginx/default.dataserver.conf:/etc/nginx/conf.d/default.conf:ro"
|
|
20
|
+
ports:
|
|
21
|
+
- "8000:80"
|
package/index.html
ADDED
package/jest/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Some Jest config (e.g. transpiling with Babel) bootstrapped from `react-scripts` ([MIT
|
|
2
|
+
License](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/LICENSE).
|
|
3
|
+
|
|
4
|
+
# TODO
|
|
5
|
+
|
|
6
|
+
* Consider using the documented `__mocks__` method to handle static assets
|
|
7
|
+
(https://jestjs.io/docs/webpack) instead of the `react-scripts` way. Everest-UI uses
|
|
8
|
+
the Jest-documented way.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Do this as the first thing so that any code reading it knows the right env.
|
|
4
|
+
process.env.BABEL_ENV = 'test';
|
|
5
|
+
process.env.NODE_ENV = 'test';
|
|
6
|
+
process.env.PUBLIC_URL = '';
|
|
7
|
+
|
|
8
|
+
// Makes the script crash on unhandled rejections instead of silently
|
|
9
|
+
// ignoring them. In the future, promise rejections that are not handled will
|
|
10
|
+
// terminate the Node.js process with a non-zero exit code.
|
|
11
|
+
process.on('unhandledRejection', err => {
|
|
12
|
+
throw err;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const jest = require('jest');
|
|
16
|
+
const execSync = require('child_process').execSync;
|
|
17
|
+
let argv = process.argv.slice(2);
|
|
18
|
+
|
|
19
|
+
jest.run(argv);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const babelJest = require('babel-jest').default;
|
|
4
|
+
|
|
5
|
+
const hasJsxRuntime = (() => {
|
|
6
|
+
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
require.resolve('react/jsx-runtime');
|
|
12
|
+
return true;
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
})();
|
|
17
|
+
|
|
18
|
+
module.exports = babelJest.createTransformer({
|
|
19
|
+
presets: [
|
|
20
|
+
[
|
|
21
|
+
require.resolve('babel-preset-react-app'),
|
|
22
|
+
{
|
|
23
|
+
runtime: hasJsxRuntime ? 'automatic' : 'classic',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
],
|
|
27
|
+
babelrc: false,
|
|
28
|
+
configFile: false,
|
|
29
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// This is a custom Jest transformer turning style imports into empty objects.
|
|
4
|
+
// http://facebook.github.io/jest/docs/en/webpack.html
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
process() {
|
|
8
|
+
return {code: 'module.exports = {};'};
|
|
9
|
+
},
|
|
10
|
+
getCacheKey() {
|
|
11
|
+
// The output is always the same.
|
|
12
|
+
return 'cssTransform';
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const camelcase = require('camelcase');
|
|
5
|
+
|
|
6
|
+
// This is a custom Jest transformer turning file imports into filenames.
|
|
7
|
+
// http://facebook.github.io/jest/docs/en/webpack.html
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
process(src, filename) {
|
|
11
|
+
const assetFilename = JSON.stringify(path.basename(filename));
|
|
12
|
+
|
|
13
|
+
if (filename.match(/\.svg$/)) {
|
|
14
|
+
// Based on how SVGR generates a component name:
|
|
15
|
+
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
|
|
16
|
+
const pascalCaseFilename = camelcase(path.parse(filename).name, {
|
|
17
|
+
pascalCase: true,
|
|
18
|
+
});
|
|
19
|
+
const componentName = `Svg${pascalCaseFilename}`;
|
|
20
|
+
return {
|
|
21
|
+
code: `const React = require('react');
|
|
22
|
+
module.exports = {
|
|
23
|
+
__esModule: true,
|
|
24
|
+
default: ${assetFilename},
|
|
25
|
+
ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
|
|
26
|
+
return {
|
|
27
|
+
$$typeof: Symbol.for('react.element'),
|
|
28
|
+
type: 'svg',
|
|
29
|
+
ref: ref,
|
|
30
|
+
key: null,
|
|
31
|
+
props: Object.assign({}, props, {
|
|
32
|
+
children: ${assetFilename}
|
|
33
|
+
})
|
|
34
|
+
};
|
|
35
|
+
}),
|
|
36
|
+
};`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {code: `module.exports = ${assetFilename};`};
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
server {
|
|
2
|
+
listen 80;
|
|
3
|
+
listen 443 ssl;
|
|
4
|
+
ssl_certificate /cert/ssl.crt;
|
|
5
|
+
ssl_certificate_key /cert/ssl.key;
|
|
6
|
+
|
|
7
|
+
server_name localhost;
|
|
8
|
+
|
|
9
|
+
real_ip_header X-Forwarded-For;
|
|
10
|
+
real_ip_recursive on;
|
|
11
|
+
|
|
12
|
+
location / {
|
|
13
|
+
root /usr/share/nginx/html;
|
|
14
|
+
autoindex on;
|
|
15
|
+
}
|
|
16
|
+
}
|