Deploying to gh-pages from @ usds/justice40-tool@3c3a8b0637 🚀
1
.adr-dir
|
@ -1 +0,0 @@
|
||||||
docs/decisions
|
|
129
.gitignore
vendored
|
@ -1,129 +0,0 @@
|
||||||
*.env
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
.pybuilder/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# pytype static type analyzer
|
|
||||||
.pytype/
|
|
||||||
|
|
||||||
# Cython debug symbols
|
|
||||||
cython_debug/
|
|
1
40194750-289e28ad174c3a577ea1.js
Normal file
1
404.html
Normal file
1
404/index.html
Normal file
|
@ -1,13 +0,0 @@
|
||||||
# Contributor Code of Conduct
|
|
||||||
|
|
||||||
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
|
||||||
|
|
||||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct.
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers at justice40open@usds.gov.
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
|
|
@ -1,48 +0,0 @@
|
||||||
# Contributing to the Justice40 Tool
|
|
||||||
|
|
||||||
🎉 First off, thanks for taking the time to contribute! 🎉
|
|
||||||
|
|
||||||
The following is a set of guidelines for contributing to the Justice40 Tool that lives in this repository.
|
|
||||||
|
|
||||||
Before contributing, we encourage you to also read our [LICENSE](https://github.com/usds/justice40-tool/blob/main/LICENSE) and [README](https://github.com/usds/justice40-tool/blob/main/README) files, also found in this repository. If you have any questions not answered by the content of this repository, please don't hesitate to [contact us](mailto:justice40open@usds.gov).
|
|
||||||
|
|
||||||
## Public Domain
|
|
||||||
|
|
||||||
This project is in the public domain within the United States, and copyright and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/).
|
|
||||||
|
|
||||||
All contributions to this project will be released under the CC0 dedication. By submitting a pull request you are agreeing to comply with this waiver of copyright interest.
|
|
||||||
|
|
||||||
## How Can I Contribute?
|
|
||||||
|
|
||||||
### Report a bug
|
|
||||||
|
|
||||||
If you think you have found a bug in the Justice40 tool, search our issues list on GitHub in case a similar issue has already been opened.
|
|
||||||
|
|
||||||
When reporting the bug, please follow these guidelines:
|
|
||||||
|
|
||||||
- **Please use the `Bug Report` issue template** ([here](https://github.com/usds/justice40-tool/issues/new/choose)). This is populated with the right information
|
|
||||||
- **Use a clear and descriptive issue title** for the issue to identify the problem.
|
|
||||||
- **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you got to the page where you encountered the bug.
|
|
||||||
- **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
|
|
||||||
- **Explain which behavior you expected to see instead and why.**
|
|
||||||
- **Include screenshots and animated GIFs** if possible, which show you following the described steps and clearly demonstrate the problem.
|
|
||||||
- **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened.
|
|
||||||
|
|
||||||
### Suggest an enhancement
|
|
||||||
|
|
||||||
If you don't have specific language or code to submit but would like to suggest a change, request a feature,
|
|
||||||
or have something addressed, you can open an issue in this repository.
|
|
||||||
|
|
||||||
Please open an issue of type "Feature request" [here](https://github.com/usds/justice40-tool/issues/new/choose).
|
|
||||||
|
|
||||||
In the issue, please describe the feature you would like to see, why you need it, and how it should work. Team members will respond to the issue as soon as possible.
|
|
||||||
|
|
||||||
### Submit a pull request
|
|
||||||
|
|
||||||
If you would like to contribute, please submit a pull request. In order for us to merge a pull request, the following checks are enabled within this repo:
|
|
||||||
|
|
||||||
- Merges to `main` are prohibited - please open a pull request from a branch
|
|
||||||
- At least one required reviewer must approve the commit (see [CODEOWNERS](https://github.com/usds/justice40-tool/tree/main/.github/CODEOWNERS) for the most up-to-date list of these members)
|
|
||||||
- All required status checks must pass
|
|
||||||
|
|
||||||
If there is significant dissent within the team, a meeting will be held to discuss a plan of action for the pull request.
|
|
25
LICENSE.md
|
@ -1,25 +0,0 @@
|
||||||
## This project is in the worldwide public domain
|
|
||||||
|
|
||||||
As a work of the United States government, this project is in the public domain within the United States.
|
|
||||||
|
|
||||||
Additionally, we waive copyright and related rights in the work worldwide through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/).
|
|
||||||
|
|
||||||
### CC0 1.0 Universal Summary
|
|
||||||
|
|
||||||
This is a human-readable summary of the [Legal Code (read the full text)](https://creativecommons.org/publicdomain/zero/1.0/legalcode).
|
|
||||||
|
|
||||||
#### No Copyright
|
|
||||||
|
|
||||||
The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his, her, or their rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
|
|
||||||
|
|
||||||
You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
|
|
||||||
|
|
||||||
#### Other Information
|
|
||||||
|
|
||||||
In no way are the patent or trademark rights of any person affected by CC0, nor are the rights that other persons may have in the work or in how the work is used, such as publicity or privacy rights.
|
|
||||||
|
|
||||||
Unless expressly stated otherwise, the person who associated a work with this deed makes no warranties about the work, and disclaims liability for all uses of the work, to the fullest extent permitted by applicable law. When using or citing the work, you should not imply endorsement by the author or the affirmer.
|
|
||||||
|
|
||||||
### Contributions to this project
|
|
||||||
|
|
||||||
As stated in [CONTRIBUTING](CONTRIBUTING.md), all contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest.
|
|
17
README.md
|
@ -1,17 +0,0 @@
|
||||||
|
|
||||||
# Justice40 Tool
|
|
||||||
[](https://github.com/usds/justice40-tool/blob/main/LICENSE)
|
|
||||||
|
|
||||||
This repo contains the code for the working prototype and soon to be MVP of the Justice40 Climate, Environmental, and Economic Justice Screeing Tool. The Justice40 initiative and screening tool were announced in an [Executive Order](https://www.whitehouse.gov/briefing-room/presidential-actions/2021/01/27/executive-order-on-tackling-the-climate-crisis-at-home-and-abroad/) in January 2021, with the goal to launch an MVP of the tool by July 27, 2021. This tool will include interactive maps and an initial draft scorecard by which federal agencies can identify historically overburdened and underserved communities for benefits in their programs.
|
|
||||||
|
|
||||||
The Justice40 team is taking a community-first and open source approach to the product development of this tool. We encourage contributions in the form of discussion on issues in this repo and pull requests of documentation and code. Please see the [Contributing](#Contributing) section for more information.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are always welcome!
|
|
||||||
|
|
||||||
See `CONTRIBUTING.md` for ways to get started.
|
|
||||||
|
|
||||||
## Feedback
|
|
||||||
|
|
||||||
If you have any feedback or questions, please reach out to us at justice40open@usds.gov.
|
|
1
app-404d7df65849d4125f83.js
Normal file
1
chunk-map.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"polyfill":["/polyfill-aab98ca4729baf333612.js"],"app":["/app-404d7df65849d4125f83.js"],"component---src-pages-404-tsx":["/component---src-pages-404-tsx-0559dc84bfa6590824cc.js"],"component---src-pages-index-tsx":["/component---src-pages-index-tsx-a57013ff3500d3516c77.js"]}
|
3
client/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
||||||
node_modules/
|
|
||||||
.cache/
|
|
||||||
public
|
|
34
client/.vscode/launch.json
vendored
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug Chrome",
|
|
||||||
"url": "http://localhost:8000",
|
|
||||||
"webRoot": "${workspaceFolder}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Gatsby develop",
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"protocol": "inspector",
|
|
||||||
"program": "${workspaceRoot}/node_modules/gatsby/dist/bin/gatsby",
|
|
||||||
"args": ["develop"],
|
|
||||||
"stopOnEntry": false,
|
|
||||||
"runtimeArgs": ["--nolazy"],
|
|
||||||
"sourceMaps": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Gatsby build",
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"protocol": "inspector",
|
|
||||||
"program": "${workspaceRoot}/node_modules/gatsby/dist/bin/gatsby",
|
|
||||||
"args": ["build"],
|
|
||||||
"stopOnEntry": false,
|
|
||||||
"runtimeArgs": ["--nolazy"],
|
|
||||||
"sourceMaps": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
# Justice40 Client
|
|
||||||
|
|
||||||
## Localization
|
|
||||||
|
|
||||||
### About
|
|
||||||
|
|
||||||
This project uses [Gatsby Plugin Intl](https://www.gatsbyjs.com/plugins/gatsby-plugin-intl/?=intl) to manage internationalization and localization.
|
|
||||||
|
|
||||||
There are a number of components to this, but for the purposes of localization, this utizes the popular `react-intl` package from [FormatJS](https://github.com/formatjs/formatjs).
|
|
||||||
|
|
||||||
This works by directing users to a locale-appropriate version of the page they wish to visit based on their browser settings, populated automatically at build time by the contents of `json` files in the `src/intl` directory.
|
|
||||||
|
|
||||||
### Writing
|
|
||||||
|
|
||||||
For this library to work optimally, the following principles should be obeyed (see [here](https://formatjs.io/docs/getting-started/message-extraction) for more detail):
|
|
||||||
|
|
||||||
- All user-visible strings should be wrapped with the `intl.formatMessage` function or the `<FormattedMessage>` tag, with a `description` and `defaultMessage` set. Do not yet set the "id" tag, it will be generated for you. To generate files for localization, run `npm run intl:extract` to update the file at `src/intl/en.json` with the extracted contents of all `FormattedMessage` components.
|
|
||||||
- Take note of the `id` in this file, and add this back as a parameter of your function/prop for your component (this is done to avoid naming collisions as detailed [here](https://formatjs.io/docs/getting-started/message-extraction))
|
|
||||||
- All `Link` components should be imported from `gatsby-plugin-intl` instead to get the locale-appropriate link
|
|
||||||
- All pages should import and use `useIntl` from `gatsby-plugin-intl`
|
|
||||||
|
|
||||||
We will later add integration with Github Actions to ensure that all messages have been formatted as a condition/check for committed code.
|
|
||||||
|
|
||||||
### Translating
|
|
||||||
|
|
||||||
From there, send `src/intl/en.json` to translators. (Depending on the TMS (Translation Management System) in use, we may need a different format, so we can alter the settings in `package.json` if needbe). When they return with the other language file, e.g. `es.json`, place this in `src/intl/` as a sibling to `en.json`.
|
|
||||||
|
|
||||||
### Consuming
|
|
||||||
|
|
||||||
`React-Intl` works according to Google SEO [best practices](https://developers.google.com/search/docs/advanced/crawling/managing-multi-regional-sites#use-different-urls-for-different-language-versions) by creating locale-specific URLs.
|
|
||||||
|
|
||||||
To access a translated version of a page, e.g. `pages/index.js`, add the locale as a portion of the URL path, as follows:
|
|
||||||
|
|
||||||
- English: `localhost:8000/en/`, or `localhost:8000/` (the default fallback is English)
|
|
|
@ -1 +0,0 @@
|
||||||
import './src/styles/global.scss';
|
|
|
@ -1,40 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
/*
|
|
||||||
This is to workaround the following error when building locally:
|
|
||||||
Warning: React.createElement: type is invalid -- expected a string
|
|
||||||
(for built-in components) or a class/function (for composite components) but got: undefined.
|
|
||||||
at IndexPage
|
|
||||||
We will need to fix this before running `gatsby build`
|
|
||||||
*/
|
|
||||||
flags: {
|
|
||||||
DEV_SSR: false
|
|
||||||
},
|
|
||||||
siteMetadata: {
|
|
||||||
title: "Justice40",
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
resolve: 'gatsby-plugin-sass',
|
|
||||||
options: {
|
|
||||||
cssLoaderOptions: {
|
|
||||||
modules: {
|
|
||||||
exportLocalsConvention: 'camelCaseOnly'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resolve: `gatsby-plugin-intl`,
|
|
||||||
options: {
|
|
||||||
// language JSON resource path
|
|
||||||
path: `${__dirname}/src/intl`,
|
|
||||||
// supported language
|
|
||||||
languages: [`en`, `es`],
|
|
||||||
// language file path
|
|
||||||
defaultLanguage: `en`,
|
|
||||||
// option to redirect to `/en` when connecting `/`
|
|
||||||
redirect: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
|
@ -1,21 +0,0 @@
|
||||||
path = require('path');
|
|
||||||
|
|
||||||
// Disable references to window from trussworks/uswds
|
|
||||||
// See here: https://www.gatsbyjs.com/docs/debugging-html-builds/#fixing-third-party-modules
|
|
||||||
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
|
|
||||||
actions.setWebpackConfig({
|
|
||||||
devtool: 'eval-source-map',
|
|
||||||
});
|
|
||||||
if (stage === "build-html" || stage === "develop-html") {
|
|
||||||
actions.setWebpackConfig({
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /@trussworks\/react-uswds/,
|
|
||||||
use: loaders.null(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
35066
client/package-lock.json
generated
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
"name": "justice40-tool",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"private": true,
|
|
||||||
"description": "Justice40 Tool",
|
|
||||||
"author": "Nat Hillard",
|
|
||||||
"keywords": [
|
|
||||||
"gatsby"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"develop": "gatsby develop",
|
|
||||||
"start": "gatsby develop",
|
|
||||||
"build": "gatsby build",
|
|
||||||
"serve": "gatsby serve",
|
|
||||||
"clean": "gatsby clean",
|
|
||||||
"intl:extract": "formatjs extract 'src/(pages|components)/*.tsx' --out-file src/intl/en.json",
|
|
||||||
"intl:compile": "formatjs compile src/intl/en.json --ast --out-file compiled-lang/en.json"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@formatjs/cli": "^4.2.15",
|
|
||||||
"@types/node": "^15.3.1",
|
|
||||||
"@types/react": "^17.0.1",
|
|
||||||
"@types/react-dom": "^17.0.1",
|
|
||||||
"gatsby-cli": "^3.5.0",
|
|
||||||
"gatsby-plugin-intl": "^0.3.3",
|
|
||||||
"gatsby-plugin-sass": "^4.5.0",
|
|
||||||
"sass": "^1.33.0",
|
|
||||||
"sass-loader": "^11.1.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@trussworks/react-uswds": "^1.17.0",
|
|
||||||
"gatsby": "^3.4.1",
|
|
||||||
"react": "^17.0.1",
|
|
||||||
"react-dom": "^17.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { Footer } from '@trussworks/react-uswds';
|
|
||||||
import {Link} from 'gatsby-plugin-intl';
|
|
||||||
|
|
||||||
const footerLinks = [
|
|
||||||
<Link to="/">Home</Link>
|
|
||||||
];
|
|
||||||
|
|
||||||
const J40Footer = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Footer primary={[]} secondary={footerLinks} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default J40Footer;
|
|
|
@ -1,25 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { GovBanner, Header, Title, PrimaryNav } from '@trussworks/react-uswds';
|
|
||||||
import { useIntl, Link } from "gatsby-plugin-intl"
|
|
||||||
|
|
||||||
const headerLinks = [
|
|
||||||
<Link to="/">Home</Link>
|
|
||||||
];
|
|
||||||
|
|
||||||
const J40Header = () => {
|
|
||||||
const intl = useIntl()
|
|
||||||
const title = intl.formatMessage({ id: "71L0pp", defaultMessage:"Justice40", description:"Title of the project" });
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<GovBanner />
|
|
||||||
<Header>
|
|
||||||
<Title>
|
|
||||||
{title}
|
|
||||||
</Title>
|
|
||||||
<PrimaryNav items={headerLinks}/>
|
|
||||||
</Header>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default J40Header;
|
|
|
@ -1,9 +0,0 @@
|
||||||
.site {
|
|
||||||
display: flex;
|
|
||||||
min-height: 100vh;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
13
client/src/components/layout.module.scss.d.ts
vendored
|
@ -1,13 +0,0 @@
|
||||||
declare namespace LayoutModuleScssNamespace {
|
|
||||||
export interface ILayoutModuleScss {
|
|
||||||
site: string;
|
|
||||||
siteContent: string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare const LayoutModuleScssModule: LayoutModuleScssNamespace.ILayoutModuleScss & {
|
|
||||||
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
|
|
||||||
locals: LayoutModuleScssNamespace.ILayoutModuleScss;
|
|
||||||
};
|
|
||||||
|
|
||||||
export = LayoutModuleScssModule;
|
|
|
@ -1,19 +0,0 @@
|
||||||
import React, { ReactNode } from "react";
|
|
||||||
import * as styles from './layout.module.scss';
|
|
||||||
import J40Header from './J40Header';
|
|
||||||
import J40Footer from "./J40Footer";
|
|
||||||
interface ILayoutProps {
|
|
||||||
children: ReactNode
|
|
||||||
}
|
|
||||||
|
|
||||||
const Layout = ({ children }: ILayoutProps) => {
|
|
||||||
return (
|
|
||||||
<div className={styles.site}>
|
|
||||||
<J40Header />
|
|
||||||
<div className={styles.siteContent}>{children}</div>
|
|
||||||
<J40Footer />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Layout;
|
|
Before Width: | Height: | Size: 11 KiB |
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"71L0pp": {
|
|
||||||
"defaultMessage": "Justice40",
|
|
||||||
"description": "Title of the project"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"71L0pp": "Justicia40"
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { Link } from "gatsby-plugin-intl";
|
|
||||||
|
|
||||||
// styles
|
|
||||||
const pageStyles = {
|
|
||||||
color: "#232129",
|
|
||||||
padding: "96px",
|
|
||||||
fontFamily: "-apple-system, Roboto, sans-serif, serif",
|
|
||||||
}
|
|
||||||
const headingStyles = {
|
|
||||||
marginTop: 0,
|
|
||||||
marginBottom: 64,
|
|
||||||
maxWidth: 320,
|
|
||||||
}
|
|
||||||
|
|
||||||
const paragraphStyles = {
|
|
||||||
marginBottom: 48,
|
|
||||||
}
|
|
||||||
const codeStyles = {
|
|
||||||
color: "#8A6534",
|
|
||||||
padding: 4,
|
|
||||||
backgroundColor: "#FFF4DB",
|
|
||||||
fontSize: "1.25rem",
|
|
||||||
borderRadius: 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
// markup
|
|
||||||
const NotFoundPage = () => {
|
|
||||||
return (
|
|
||||||
<main style={pageStyles}>
|
|
||||||
<title>Not found</title>
|
|
||||||
<h1 style={headingStyles}>Page not found</h1>
|
|
||||||
<p style={paragraphStyles}>
|
|
||||||
Sorry{" "}
|
|
||||||
<span role="img" aria-label="Pensive emoji">
|
|
||||||
😔
|
|
||||||
</span>{" "}
|
|
||||||
we couldn’t find what you were looking for.
|
|
||||||
<br />
|
|
||||||
{process.env.NODE_ENV === "development" ? (
|
|
||||||
<>
|
|
||||||
<br />
|
|
||||||
Try creating a page in <code style={codeStyles}>src/pages/</code>.
|
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
<br />
|
|
||||||
<Link to="/">Go home</Link>.
|
|
||||||
</p>
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NotFoundPage
|
|
|
@ -1,15 +0,0 @@
|
||||||
import * as React from "react"
|
|
||||||
import Layout from '../components/layout';
|
|
||||||
// markup
|
|
||||||
const IndexPage = () => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<main>
|
|
||||||
<h2>Subheader</h2>
|
|
||||||
</main>
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IndexPage;
|
|
|
@ -1,2 +0,0 @@
|
||||||
@import "~@trussworks/react-uswds/lib/uswds.css";
|
|
||||||
@import "~@trussworks/react-uswds/lib/index.css";
|
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es6",
|
|
||||||
"jsx": "preserve",
|
|
||||||
"lib": ["dom", "es2015", "es2017"],
|
|
||||||
"strict": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"removeComments": false
|
|
||||||
},
|
|
||||||
"include": ["./src/**/*"]
|
|
||||||
}
|
|
1
component---src-pages-404-tsx-0559dc84bfa6590824cc.js
Normal file
1
component---src-pages-index-tsx-a57013ff3500d3516c77.js
Normal file
|
@ -1,153 +0,0 @@
|
||||||
# Overview
|
|
||||||
|
|
||||||
This document describes our "data roadmap", which serves several purposes.
|
|
||||||
|
|
||||||
# Data roadmap goals
|
|
||||||
|
|
||||||
The goals of the data roadmap are as follows:
|
|
||||||
|
|
||||||
- Tracking data sets being considered for inclusion in the Climate and Economic Justice Screening Tool (CEJST), either as a data set that is included in the cumulative impacts score or a reference data set that is not included in the score
|
|
||||||
|
|
||||||
- Prioritizing data sets, so that it's obvious to developers working on the CEJST which data sets to incorporate next into the tool
|
|
||||||
|
|
||||||
- Gathering important details about each data set, such as its geographic resolution and the year it was last updated, so that the CEJST team can make informed decisions about what data to prioritize
|
|
||||||
|
|
||||||
- Tracking the problem areas that each data set relates to (e.g., a certain data set may relate to the problem of pesticide exposure amongst migrant farm workers)
|
|
||||||
|
|
||||||
- Enabling members of the public to submit ideas for problem areas or data sets to be considered for inclusion in the CEJST, with easy-to-use and accessible tools
|
|
||||||
|
|
||||||
- Enabling members of the public to submit revisions to the information about each problem area or data set, with easy-to-use and accessible tools
|
|
||||||
|
|
||||||
- Enabling the CEJST development team to review suggestions before incorporating them officially into the data roadmap, to filter out potential noise and spam, or consider how requests may lead to changes in software features and documentation
|
|
||||||
|
|
||||||
# User stories
|
|
||||||
|
|
||||||
These goals can map onto several user stories for the data roadmap, such as:
|
|
||||||
|
|
||||||
- As a community member, I want to suggest a new idea for a dataset.
|
|
||||||
- As a community member, I want to understand what happened with my suggestion for a new dataset.
|
|
||||||
- As a community member, I want to edit the details of a dataset proposal to add more information.
|
|
||||||
- As a WHEJAC board member, I want to vote on what data sources should be prioritized next.
|
|
||||||
- As a product manager, I want to filter based on characteristics of the data.
|
|
||||||
- As a developer, I want to know what to work on next.
|
|
||||||
|
|
||||||
# Data set descriptions
|
|
||||||
|
|
||||||
There are lots of details that are important to track for each data set. This
|
|
||||||
information helps us prepare to integrate a data set into the tool and prioritize
|
|
||||||
between different options for data in the data roadmap.
|
|
||||||
|
|
||||||
In order to support a process of peer review on edits and updates, these details are
|
|
||||||
tracked in one `YAML` file per data set description in the directory
|
|
||||||
[data_roadmap/data_set_descriptions](data_roadmap/data_set_descriptions).
|
|
||||||
|
|
||||||
Each data set description includes a number of fields, some of which are required.
|
|
||||||
The schema defining these fields is written in [Yamale](https://github.com/23andMe/Yamale)
|
|
||||||
and lives at [data_roadmap/data_set_description_schema.yaml](data_roadmap/data_set_description_schema.yaml).
|
|
||||||
|
|
||||||
Because `Yamale` does not provide a method for describing fields, we've created an
|
|
||||||
additional file that includes written descriptions of the meaning of each field in
|
|
||||||
the schema. These live in [data_roadmap/data_set_description_field_descriptions.yaml](data_roadmap/data_set_description_field_descriptions.yaml).
|
|
||||||
|
|
||||||
In order to provide a helpful starting point for people who are ready to contribute
|
|
||||||
ideas for a new data set for consideration, there is an auto-generated data set
|
|
||||||
description template that lives at [data_roadmap/data_set_description_template.yaml](data_roadmap/data_set_description_template.yaml).
|
|
||||||
|
|
||||||
# Steps to add a new data set description: the "easy" way
|
|
||||||
|
|
||||||
Soon we will create a Google Form that contributors can use to submit ideas for new
|
|
||||||
data sets. The Google Form will match the schema of the data set descriptions. Please
|
|
||||||
see [this ticket](https://app.zenhub.com/workspaces/justice40-60993f6e05473d0010ec44e3/issues/usds/justice40-tool/39)
|
|
||||||
for tracking this work.
|
|
||||||
|
|
||||||
# Steps to add a new data set description: the git-savvy way
|
|
||||||
|
|
||||||
For those who are comfortable using `git` and `Markdown`, these are the steps to
|
|
||||||
contribute a new data set description to the data roadmap:
|
|
||||||
|
|
||||||
1. Research and learn about the data set you're proposing for consideration.
|
|
||||||
|
|
||||||
2. Clone the repository and learn about the [contribution guidelines for this
|
|
||||||
project](../docs/CONTRIBUTING.md).
|
|
||||||
|
|
||||||
3. In your local version of the repository, copy the template from
|
|
||||||
`data_roadmap/data_set_description_template.yaml` into a new file that lives in
|
|
||||||
`data_roadmap/data_set_descriptions` and has the name of the data set as the name of the file.
|
|
||||||
|
|
||||||
4. Edit this file to ensure it has all of the appropriate details about the data set.
|
|
||||||
|
|
||||||
5. If you'd like, you can run the validations in `run_validations_and_write_template`
|
|
||||||
to ensure your contribution is valid according to the schema. These checks will also
|
|
||||||
run automatically on each commit.
|
|
||||||
|
|
||||||
6. Create a pull request with your new data set description and submit it for peer
|
|
||||||
review.
|
|
||||||
|
|
||||||
Thank you for contributing!
|
|
||||||
|
|
||||||
# Tooling proposal and milestones
|
|
||||||
|
|
||||||
There is no single tool that supports all the goals and user stories described above.
|
|
||||||
Therefore we've proposed combining a number of tools in a way that can support them all.
|
|
||||||
|
|
||||||
We've also proposed various "milestones" that will allow us to iteratively and
|
|
||||||
sequentially build the data roadmap in a way that supports the entire vision but
|
|
||||||
starts with small and achievable steps. These milestones are proposed in order.
|
|
||||||
|
|
||||||
This work is most accurately tracked in [this epic](https://app.zenhub.com/workspaces/justice40-60993f6e05473d0010ec44e3/issues/usds/justice40-tool/38).
|
|
||||||
We've also verbally described them below.
|
|
||||||
|
|
||||||
## Milestone: YAML files for data sets and linter (Done)
|
|
||||||
|
|
||||||
To start, we'll create a folder in this repository that can
|
|
||||||
house YAML files, one per data set. Each file will describe the characteristics of the data.
|
|
||||||
|
|
||||||
The benefit of using a YAML file for this is that it's easy to subject changes to these files to peer review through the pull request process. This allows external collaborators from the open source community to submit suggested changes, which can be reviewed by the core CEJST team.
|
|
||||||
|
|
||||||
We'll use a Python-based script to load all the files in the directory, and then run a schema validator to ensure all the files have valid entries.
|
|
||||||
|
|
||||||
For schema validation, we propose using [Yamale](https://github.com/23andMe/Yamale). This provides a lightweight schema and validator, and [integrates nicely with GitHub actions](https://github.com/nrkno/yaml-schema-validator-github-action).
|
|
||||||
|
|
||||||
If there's an improper format in any of the files, the schema validator will throw an error.
|
|
||||||
|
|
||||||
As part of this milestone, we will also set this up to run automatically with each commit to any branch as part of CI/CD.
|
|
||||||
|
|
||||||
## Milestone: Google forms integration
|
|
||||||
|
|
||||||
To make it easy for non-engineer members of the public and advisory bodies such as the WHEJAC to submit suggestions for data sets, we will configure a Google Form that maps to the schema of the data set files.
|
|
||||||
|
|
||||||
This will enable members of the public to fill out a simple form suggesting data without needing to understand Github or other engineering concepts.
|
|
||||||
|
|
||||||
At first, these responses can just go into a resulting Google Sheet and be manually copied and converted into data set description files. Later, we can write a script that converts new entries in the Google Sheet automatically into data set files. This can be setup to run as a trigger on the addition of new rows to the Google Sheet.
|
|
||||||
|
|
||||||
## Milestone: Post data in tabular format
|
|
||||||
|
|
||||||
Add a script that runs the schema validator on all files and, if successful, posts the results in a tabular format. There are straightforward packages to post a Python dictionary / `pandas` dataframe to Google Sheets and/or Airtable. As part of this milestone, we will also set this up to run automatically with each commit to `main` as part of CI/CD.
|
|
||||||
|
|
||||||
This will make it easier to filter the data to answer questions like, "which data sources are available at the census block group level".
|
|
||||||
|
|
||||||
## Milestone: Tickets created for incorporating data sets
|
|
||||||
|
|
||||||
For each data set that is being considered for inclusion soon in the tool, the project management team will create a ticket for "Incorporating \_\_\_ data set into the database", with a link to the data set detail document. This ticket will be created in the ticket tracking system used by the open source repository, which is ZenHub. This project management system will be public.
|
|
||||||
|
|
||||||
At the initial launch, we are not planning for members of the open source community to be able to create tickets, but we would like to consider a process for members of the open source community creating tickets that can go through review by the CEJST team.
|
|
||||||
|
|
||||||
This will help developers know what to work on next, and open source community members can also pick up tickets and work to integrate the data sets.
|
|
||||||
|
|
||||||
## Milestone: Add problem areas
|
|
||||||
|
|
||||||
We'll need to somehow track "problem areas" that describe problems in climate, environmental, and economic justice, even without specific proposals of data sets. For instance, a problem area may be "food insecurity", and a number of data sets can have this as their problem area.
|
|
||||||
|
|
||||||
We can change the linter to validate that every data set description maps to one or more known problem areas.
|
|
||||||
|
|
||||||
The benefit of this is that some non-data-focused members of the public or the WHEJAC advisory body may want to suggest we prioritize certain problem areas, with or without ideas for specific data sets that may best address that problem area.
|
|
||||||
|
|
||||||
It is not clear at this time the best path forward for implementing these problem area descriptions. One option is to create a folder for descriptions of problem areas, which contains YAML files that get validated according to a schema. Another option would be simply to add these as an array in the description of data sets, or add labels to the tickets once data sets are tracked in GitHub tickets.
|
|
||||||
|
|
||||||
## Milestone: Add prioritzation voting for WHEJAC and members of the public
|
|
||||||
|
|
||||||
This milestone is currently the least well-defined. It's important that members of advisory bodies like the WHEJAC and members of the public be able to "upvote" certain data sets for inclusion in the tool.
|
|
||||||
|
|
||||||
One potential for this is to use the [Stanford Participatory Budgeting Platform](https://pbstanford.org/). Here's an [example of voting on proposals within a limited budget](https://pbstanford.org/nyc8/knapsack).
|
|
||||||
|
|
||||||
For instance, going into a quarterly planning cycle, the CEJST development team could estimate the amount of time (in developer-weeks) that it would take to clean, analyze, and incorporate each potential data set. For instance, incorporating some already-cleaned census data may take 1 week of a developer's time, while incorporating new asthma data from CMS that's never been publicly released could take 5 weeks. Given a "budget" of the number of developer weeks available (e.g., 2 developers for 13 weeks, or 26 developer-weeks), advisors can vote on their top priorities for inclusion in the tool within the available "budget".
|
|
|
@ -1,39 +0,0 @@
|
||||||
# There is no method for adding field descriptions to `yamale` schemas.
|
|
||||||
# Therefore, we've created a dictionary here of fields and their descriptions.
|
|
||||||
name: A short name of the data set.
|
|
||||||
source: The URL pointing towards the data set itself or more information about the
|
|
||||||
data set.
|
|
||||||
relevance_to_environmental_justice: It's useful to spell out why this data is
|
|
||||||
relevant to EJ issues and/or can be used to identify EJ communities.
|
|
||||||
spatial_resolution: Dev team needs to know if the resolution is granular enough to be useful
|
|
||||||
public_status: Whether a dataset has already gone through public release process
|
|
||||||
(like Census data) or may need a lengthy review process (like Medicaid data).
|
|
||||||
sponsor: Whether there's a federal agency or non-governmental agency that is working
|
|
||||||
to provide and maintain this data.
|
|
||||||
subjective_rating_of_data_quality: Sometimes we don't have statistics on data
|
|
||||||
quality, but we know it is likely to be accurate or not. How much has it been
|
|
||||||
vetted by an agency; is this the de facto data set for the topic?
|
|
||||||
estimated_margin_of_error: Estimated margin of error on measurement, if known. Often
|
|
||||||
more narrow geographic measures have a higher margin of error due to a smaller sample
|
|
||||||
for each measurement.
|
|
||||||
known_data_quality_issues: It can be helpful to write out known problems.
|
|
||||||
geographic_coverage_percent: We want to think about data that is comprehensive across
|
|
||||||
America.
|
|
||||||
geographic_coverage_description: A verbal description of geographic coverage.
|
|
||||||
data_formats: Developers need to know what formats the data is available in
|
|
||||||
last_updated_date: When was the data last updated / refreshed? (In format YYYY-MM-DD.
|
|
||||||
If exact date is not known, use YYYY-01-01.)
|
|
||||||
frequency_of_updates: How often is this data updated? Is it updated on a reliable
|
|
||||||
cadence?
|
|
||||||
documentation: Link to docs. Also, is the documentation good enough? Can we get the
|
|
||||||
info we need?
|
|
||||||
data_can_go_in_cloud: Some datasets can not legally go in the cloud
|
|
||||||
|
|
||||||
discussion: Review of other topics, such as
|
|
||||||
peer review (Overview or links out to peer review done on this dataset),
|
|
||||||
where and how data is available (e.g., Geoplatform.gov? Is it available from multiple
|
|
||||||
sources?),
|
|
||||||
risk assessment of the data (e.g. a vendor-processed version of the dataset might not
|
|
||||||
be open or good enough),
|
|
||||||
legal considerations (Legal disclaimers, assumption of risk, proprietary?),
|
|
||||||
accreditation (Is this source accredited?)
|
|
|
@ -1,24 +0,0 @@
|
||||||
# `yamale` schema for descriptions of data sets.
|
|
||||||
name: str(required=True)
|
|
||||||
source: str(required=True)
|
|
||||||
relevance_to_environmental_justice: str(required=False)
|
|
||||||
data_formats: enum('GeoJSON', 'Esri Shapefile (SHP, DBF, SHX)', 'GML', 'KML/KMZ',
|
|
||||||
'GPX', 'CSV/XLSX', 'GDB', 'MBTILES', 'LAS', required=True)
|
|
||||||
spatial_resolution: enum('State/territory', 'County', 'Zip code', 'Census tract',
|
|
||||||
'Census block group', 'Exact address or lat/long', 'Other', required=True)
|
|
||||||
public_status: enum('Not Released', 'Public', 'Public for certain audiences', 'Other',
|
|
||||||
required=True)
|
|
||||||
sponsor: str(required=True)
|
|
||||||
subjective_rating_of_data_quality: enum('Low Quality', 'Medium Quality', 'High
|
|
||||||
Quality', required=False)
|
|
||||||
estimated_margin_of_error: num(required=False)
|
|
||||||
known_data_quality_issues: str(required=False)
|
|
||||||
geographic_coverage_percent: num(required=False)
|
|
||||||
geographic_coverage_description: str(required=False)
|
|
||||||
last_updated_date: day(min='2001-01-01', max='2100-01-01', required=True)
|
|
||||||
frequency_of_updates: enum('Less than annually', 'Approximately annually',
|
|
||||||
'Once very 1-6 months',
|
|
||||||
'Daily or more frequently than daily', 'Unknown', required=True)
|
|
||||||
documentation: str(required=False)
|
|
||||||
data_can_go_in_cloud: bool(required=False)
|
|
||||||
discussion: str(required=False)
|
|
|
@ -1,94 +0,0 @@
|
||||||
# Note: This template is automatically generated by the function
|
|
||||||
# `write_data_set_description_template_file` from the schema
|
|
||||||
# and field descriptions files. Do not manually edit this file.
|
|
||||||
|
|
||||||
name:
|
|
||||||
# Description: A short name of the data set.
|
|
||||||
# Required field: True
|
|
||||||
# Field type: str
|
|
||||||
|
|
||||||
source:
|
|
||||||
# Description: The URL pointing towards the data set itself or more information about the data set.
|
|
||||||
# Required field: True
|
|
||||||
# Field type: str
|
|
||||||
|
|
||||||
relevance_to_environmental_justice:
|
|
||||||
# Description: It's useful to spell out why this data is relevant to EJ issues and/or can be used to identify EJ communities.
|
|
||||||
# Required field: False
|
|
||||||
# Field type: str
|
|
||||||
|
|
||||||
data_formats:
|
|
||||||
# Description: Developers need to know what formats the data is available in
|
|
||||||
# Required field: True
|
|
||||||
# Field type: enum
|
|
||||||
# Valid choices are one of the following: ('GeoJSON', 'Esri Shapefile (SHP, DBF, SHX)', 'GML', 'KML/KMZ', 'GPX', 'CSV/XLSX', 'GDB', 'MBTILES', 'LAS')
|
|
||||||
|
|
||||||
spatial_resolution:
|
|
||||||
# Description: Dev team needs to know if the resolution is granular enough to be useful
|
|
||||||
# Required field: True
|
|
||||||
# Field type: enum
|
|
||||||
# Valid choices are one of the following: ('State/territory', 'County', 'Zip code', 'Census tract', 'Census block group', 'Exact address or lat/long', 'Other')
|
|
||||||
|
|
||||||
public_status:
|
|
||||||
# Description: Whether a dataset has already gone through public release process (like Census data) or may need a lengthy review process (like Medicaid data).
|
|
||||||
# Required field: True
|
|
||||||
# Field type: enum
|
|
||||||
# Valid choices are one of the following: ('Not Released', 'Public', 'Public for certain audiences', 'Other')
|
|
||||||
|
|
||||||
sponsor:
|
|
||||||
# Description: Whether there's a federal agency or non-governmental agency that is working to provide and maintain this data.
|
|
||||||
# Required field: True
|
|
||||||
# Field type: str
|
|
||||||
|
|
||||||
subjective_rating_of_data_quality:
|
|
||||||
# Description: Sometimes we don't have statistics on data quality, but we know it is likely to be accurate or not. How much has it been vetted by an agency; is this the de facto data set for the topic?
|
|
||||||
# Required field: False
|
|
||||||
# Field type: enum
|
|
||||||
# Valid choices are one of the following: ('Low Quality', 'Medium Quality', 'High Quality')
|
|
||||||
|
|
||||||
estimated_margin_of_error:
|
|
||||||
# Description: Estimated margin of error on measurement, if known. Often more narrow geographic measures have a higher margin of error due to a smaller sample for each measurement.
|
|
||||||
# Required field: False
|
|
||||||
# Field type: num
|
|
||||||
|
|
||||||
known_data_quality_issues:
|
|
||||||
# Description: It can be helpful to write out known problems.
|
|
||||||
# Required field: False
|
|
||||||
# Field type: str
|
|
||||||
|
|
||||||
geographic_coverage_percent:
|
|
||||||
# Description: We want to think about data that is comprehensive across America.
|
|
||||||
# Required field: False
|
|
||||||
# Field type: num
|
|
||||||
|
|
||||||
geographic_coverage_description:
|
|
||||||
# Description: A verbal description of geographic coverage.
|
|
||||||
# Required field: False
|
|
||||||
# Field type: str
|
|
||||||
|
|
||||||
last_updated_date:
|
|
||||||
# Description: When was the data last updated / refreshed? (In format YYYY-MM-DD. If exact date is not known, use YYYY-01-01.)
|
|
||||||
# Required field: True
|
|
||||||
# Field type: day
|
|
||||||
|
|
||||||
frequency_of_updates:
|
|
||||||
# Description: How often is this data updated? Is it updated on a reliable cadence?
|
|
||||||
# Required field: True
|
|
||||||
# Field type: enum
|
|
||||||
# Valid choices are one of the following: ('Less than annually', 'Approximately annually', 'Once very 1-6 months', 'Daily or more frequently than daily', 'Unknown')
|
|
||||||
|
|
||||||
documentation:
|
|
||||||
# Description: Link to docs. Also, is the documentation good enough? Can we get the info we need?
|
|
||||||
# Required field: False
|
|
||||||
# Field type: str
|
|
||||||
|
|
||||||
data_can_go_in_cloud:
|
|
||||||
# Description: Some datasets can not legally go in the cloud
|
|
||||||
# Required field: False
|
|
||||||
# Field type: bool
|
|
||||||
|
|
||||||
discussion:
|
|
||||||
# Description: Review of other topics, such as peer review (Overview or links out to peer review done on this dataset), where and how data is available (e.g., Geoplatform.gov? Is it available from multiple sources?), risk assessment of the data (e.g. a vendor-processed version of the dataset might not be open or good enough), legal considerations (Legal disclaimers, assumption of risk, proprietary?), accreditation (Is this source accredited?)
|
|
||||||
# Required field: False
|
|
||||||
# Field type: str
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
name: Particulate Matter 2.5
|
|
||||||
|
|
||||||
source: https://gaftp.epa.gov/EJSCREEN/
|
|
||||||
|
|
||||||
relevance_to_environmental_justice: Particulate matter has a lot of adverse impacts
|
|
||||||
on health.
|
|
||||||
|
|
||||||
data_formats: CSV/XLSX
|
|
||||||
|
|
||||||
spatial_resolution: Census block group
|
|
||||||
|
|
||||||
public_status: Public
|
|
||||||
|
|
||||||
sponsor: EPA
|
|
||||||
|
|
||||||
subjective_rating_of_data_quality: Medium Quality
|
|
||||||
|
|
||||||
estimated_margin_of_error:
|
|
||||||
|
|
||||||
known_data_quality_issues: Many PM 2.5 stations are known to be pretty far apart, so
|
|
||||||
averaging them can lead to data quality loss.
|
|
||||||
|
|
||||||
geographic_coverage_percent:
|
|
||||||
|
|
||||||
geographic_coverage_description:
|
|
||||||
|
|
||||||
last_updated_date: 2017-01-01
|
|
||||||
|
|
||||||
frequency_of_updates: Less than annually
|
|
||||||
|
|
||||||
documentation: https://www.epa.gov/sites/production/files/2015-05/documents/ejscreen_technical_document_20150505.pdf#page=13
|
|
||||||
|
|
||||||
data_can_go_in_cloud: True
|
|
||||||
|
|
||||||
discussion:
|
|
|
@ -1,151 +0,0 @@
|
||||||
import importlib_resources
|
|
||||||
import pathlib
|
|
||||||
import yamale
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
# Set directories.
|
|
||||||
DATA_ROADMAP_DIRECTORY = importlib_resources.files("data_roadmap")
|
|
||||||
UTILS_DIRECTORY = DATA_ROADMAP_DIRECTORY / "utils"
|
|
||||||
DATA_SET_DESCRIPTIONS_DIRECTORY = DATA_ROADMAP_DIRECTORY / "data_set_descriptions"
|
|
||||||
|
|
||||||
# Set file paths.
|
|
||||||
DATA_SET_DESCRIPTION_SCHEMA_FILE_PATH = (
|
|
||||||
DATA_ROADMAP_DIRECTORY / "data_set_description_schema.yaml"
|
|
||||||
)
|
|
||||||
DATA_SET_DESCRIPTION_FIELD_DESCRIPTIONS_FILE_PATH = (
|
|
||||||
DATA_ROADMAP_DIRECTORY / "data_set_description_field_descriptions.yaml"
|
|
||||||
)
|
|
||||||
DATA_SET_DESCRIPTION_TEMPLATE_FILE_PATH = (
|
|
||||||
DATA_ROADMAP_DIRECTORY / "data_set_description_template.yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def load_data_set_description_schema(
|
|
||||||
file_path: pathlib.PosixPath = DATA_SET_DESCRIPTION_SCHEMA_FILE_PATH,
|
|
||||||
) -> yamale.schema.schema.Schema:
|
|
||||||
"""Load from file the data set description schema."""
|
|
||||||
schema = yamale.make_schema(path=file_path)
|
|
||||||
|
|
||||||
return schema
|
|
||||||
|
|
||||||
|
|
||||||
def load_data_set_description_field_descriptions(
|
|
||||||
file_path: pathlib.PosixPath = DATA_SET_DESCRIPTION_FIELD_DESCRIPTIONS_FILE_PATH,
|
|
||||||
) -> dict:
|
|
||||||
"""Load from file the descriptions of fields in the data set description."""
|
|
||||||
# Load field descriptions.
|
|
||||||
with open(file_path, "r") as stream:
|
|
||||||
data_set_description_field_descriptions = yaml.safe_load(stream=stream)
|
|
||||||
|
|
||||||
return data_set_description_field_descriptions
|
|
||||||
|
|
||||||
|
|
||||||
def validate_descriptions_for_schema(
|
|
||||||
schema: yamale.schema.schema.Schema,
|
|
||||||
field_descriptions: dict,
|
|
||||||
) -> None:
|
|
||||||
"""Validate descriptions for schema.
|
|
||||||
|
|
||||||
Checks that every field in the `yamale` schema also has a field
|
|
||||||
description in the `field_descriptions` dict.
|
|
||||||
"""
|
|
||||||
for field_name in schema.dict.keys():
|
|
||||||
if field_name not in field_descriptions:
|
|
||||||
raise ValueError(
|
|
||||||
f"Field `{field_name}` does not have a "
|
|
||||||
f"description. Please add one to file `{DATA_SET_DESCRIPTION_FIELD_DESCRIPTIONS_FILE_PATH}`"
|
|
||||||
)
|
|
||||||
|
|
||||||
for field_name in field_descriptions.keys():
|
|
||||||
if field_name not in schema.dict.keys():
|
|
||||||
raise ValueError(
|
|
||||||
f"Field `{field_name}` has a description but is not in the " f"schema."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_all_data_set_descriptions(
|
|
||||||
data_set_description_schema: yamale.schema.schema.Schema,
|
|
||||||
) -> None:
|
|
||||||
"""Validate data set descriptions.
|
|
||||||
|
|
||||||
Validate each file in the `data_set_descriptions` directory the schema
|
|
||||||
against the provided schema.
|
|
||||||
|
|
||||||
"""
|
|
||||||
data_set_description_file_paths_generator = DATA_SET_DESCRIPTIONS_DIRECTORY.glob(
|
|
||||||
"*.yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Validate each file
|
|
||||||
for file_path in data_set_description_file_paths_generator:
|
|
||||||
print(f"Validating {file_path}...")
|
|
||||||
|
|
||||||
# Create a yamale Data object
|
|
||||||
data_set_description = yamale.make_data(file_path)
|
|
||||||
|
|
||||||
# TODO: explore collecting all errors and raising them at once. - Lucas
|
|
||||||
yamale.validate(schema=data_set_description_schema, data=data_set_description)
|
|
||||||
|
|
||||||
|
|
||||||
def write_data_set_description_template_file(
|
|
||||||
data_set_description_schema: yamale.schema.schema.Schema,
|
|
||||||
data_set_description_field_descriptions: dict,
|
|
||||||
template_file_path: str = DATA_SET_DESCRIPTION_TEMPLATE_FILE_PATH,
|
|
||||||
) -> None:
|
|
||||||
"""Write an example data set description with helpful comments."""
|
|
||||||
template_file_lines = []
|
|
||||||
|
|
||||||
# Write comments at the top of the template
|
|
||||||
template_file_lines.append(
|
|
||||||
"# Note: This template is automatically generated by the function\n"
|
|
||||||
"# `write_data_set_description_template_file` from the schema\n"
|
|
||||||
"# and field descriptions files. Do not manually edit this file.\n\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
schema_dict = data_set_description_schema.dict
|
|
||||||
for field_name, field_schema in schema_dict.items():
|
|
||||||
template_file_lines.append(f"{field_name}: \n")
|
|
||||||
template_file_lines.append(
|
|
||||||
f"# Description: {data_set_description_field_descriptions[field_name]}\n"
|
|
||||||
)
|
|
||||||
template_file_lines.append(f"# Required field: {field_schema.is_required}\n")
|
|
||||||
template_file_lines.append(f"# Field type: {field_schema.get_name()}\n")
|
|
||||||
if type(field_schema) is yamale.validators.validators.Enum:
|
|
||||||
template_file_lines.append(
|
|
||||||
f"# Valid choices are one of the following: {field_schema.enums}\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add an empty linebreak to separate fields.
|
|
||||||
template_file_lines.append("\n")
|
|
||||||
|
|
||||||
with open(template_file_path, "w") as file:
|
|
||||||
file.writelines(template_file_lines)
|
|
||||||
|
|
||||||
|
|
||||||
def run_validations_and_write_template() -> None:
|
|
||||||
"""Run validations of schema and descriptions, and write a template file."""
|
|
||||||
# Load the schema and a separate dictionary
|
|
||||||
data_set_description_schema = load_data_set_description_schema()
|
|
||||||
data_set_description_field_descriptions = (
|
|
||||||
load_data_set_description_field_descriptions()
|
|
||||||
)
|
|
||||||
|
|
||||||
validate_descriptions_for_schema(
|
|
||||||
schema=data_set_description_schema,
|
|
||||||
field_descriptions=data_set_description_field_descriptions,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Validate all data set descriptions in the directory against schema.
|
|
||||||
validate_all_data_set_descriptions(
|
|
||||||
data_set_description_schema=data_set_description_schema
|
|
||||||
)
|
|
||||||
|
|
||||||
# Write an example template for data set descriptions.
|
|
||||||
write_data_set_description_template_file(
|
|
||||||
data_set_description_schema=data_set_description_schema,
|
|
||||||
data_set_description_field_descriptions=data_set_description_field_descriptions,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
run_validations_and_write_template()
|
|
|
@ -1,248 +0,0 @@
|
||||||
import unittest
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
import yamale
|
|
||||||
from data_roadmap.utils.utils_data_set_description_schema import (
|
|
||||||
load_data_set_description_schema,
|
|
||||||
load_data_set_description_field_descriptions,
|
|
||||||
validate_descriptions_for_schema,
|
|
||||||
validate_all_data_set_descriptions,
|
|
||||||
write_data_set_description_template_file,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UtilsDataSetDescriptionSchema(unittest.TestCase):
|
|
||||||
@mock.patch("yamale.make_schema")
|
|
||||||
def test_load_data_set_description_schema(self, make_schema_mock):
|
|
||||||
load_data_set_description_schema(file_path="mock.yaml")
|
|
||||||
|
|
||||||
make_schema_mock.assert_called_once_with(path="mock.yaml")
|
|
||||||
|
|
||||||
@mock.patch("yaml.safe_load")
|
|
||||||
def test_load_data_set_description_field_descriptions(self, yaml_safe_load_mock):
|
|
||||||
# Note: this isn't a great test, we could mock the actual YAML to
|
|
||||||
# make it better. - Lucas
|
|
||||||
mock_dict = {
|
|
||||||
"name": "The name of the thing.",
|
|
||||||
"age": "The age of the thing.",
|
|
||||||
"height": "The height of the thing.",
|
|
||||||
"awesome": "The awesome of the thing.",
|
|
||||||
"field": "The field of the thing.",
|
|
||||||
}
|
|
||||||
|
|
||||||
yaml_safe_load_mock.return_value = mock_dict
|
|
||||||
|
|
||||||
field_descriptions = load_data_set_description_field_descriptions()
|
|
||||||
|
|
||||||
yaml_safe_load_mock.assert_called_once()
|
|
||||||
|
|
||||||
self.assertDictEqual(field_descriptions, mock_dict)
|
|
||||||
|
|
||||||
def test_validate_descriptions_for_schema(self):
|
|
||||||
# Test when all descriptions are present.
|
|
||||||
field_descriptions = {
|
|
||||||
"name": "The name of the thing.",
|
|
||||||
"age": "The age of the thing.",
|
|
||||||
"height": "The height of the thing.",
|
|
||||||
"awesome": "The awesome of the thing.",
|
|
||||||
"field": "The field of the thing.",
|
|
||||||
}
|
|
||||||
|
|
||||||
schema = yamale.make_schema(
|
|
||||||
content="""
|
|
||||||
name: str()
|
|
||||||
age: int(max=200)
|
|
||||||
height: num()
|
|
||||||
awesome: bool()
|
|
||||||
field: enum('option 1', 'option 2')
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
# Should pass.
|
|
||||||
validate_descriptions_for_schema(
|
|
||||||
schema=schema, field_descriptions=field_descriptions
|
|
||||||
)
|
|
||||||
|
|
||||||
field_descriptions_missing_one = {
|
|
||||||
"name": "The name of the thing.",
|
|
||||||
"age": "The age of the thing.",
|
|
||||||
"height": "The height of the thing.",
|
|
||||||
"awesome": "The awesome of the thing.",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Should fail because of the missing field description.
|
|
||||||
with self.assertRaises(ValueError) as context_manager:
|
|
||||||
validate_descriptions_for_schema(
|
|
||||||
schema=schema, field_descriptions=field_descriptions_missing_one
|
|
||||||
)
|
|
||||||
|
|
||||||
# Using `assertIn` because the file path is returned in the error
|
|
||||||
# message, and it varies based on environment.
|
|
||||||
self.assertIn(
|
|
||||||
"Field `field` does not have a description. Please add one to file",
|
|
||||||
str(context_manager.exception),
|
|
||||||
)
|
|
||||||
|
|
||||||
field_descriptions_extra_one = {
|
|
||||||
"name": "The name of the thing.",
|
|
||||||
"age": "The age of the thing.",
|
|
||||||
"height": "The height of the thing.",
|
|
||||||
"awesome": "The awesome of the thing.",
|
|
||||||
"field": "The field of the thing.",
|
|
||||||
"extra": "Extra description.",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Should fail because of the extra field description.
|
|
||||||
with self.assertRaises(ValueError) as context_manager:
|
|
||||||
validate_descriptions_for_schema(
|
|
||||||
schema=schema, field_descriptions=field_descriptions_extra_one
|
|
||||||
)
|
|
||||||
|
|
||||||
# Using `assertIn` because the file path is returned in the error
|
|
||||||
# message, and it varies based on environment.
|
|
||||||
self.assertEquals(
|
|
||||||
"Field `extra` has a description but is not in the schema.",
|
|
||||||
str(context_manager.exception),
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_validate_all_data_set_descriptions(self):
|
|
||||||
# Setup a few examples of `yamale` data *before* we mock the `make_data`
|
|
||||||
# function.
|
|
||||||
valid_data = yamale.make_data(
|
|
||||||
content="""
|
|
||||||
name: Bill
|
|
||||||
age: 26
|
|
||||||
height: 6.2
|
|
||||||
awesome: True
|
|
||||||
field: option 1
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
invalid_data_1 = yamale.make_data(
|
|
||||||
content="""
|
|
||||||
name: Bill
|
|
||||||
age: asdf
|
|
||||||
height: 6.2
|
|
||||||
awesome: asdf
|
|
||||||
field: option 1
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
invalid_data_2 = yamale.make_data(
|
|
||||||
content="""
|
|
||||||
age: 26
|
|
||||||
height: 6.2
|
|
||||||
awesome: True
|
|
||||||
field: option 1
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
# Mock `make_data`.
|
|
||||||
with mock.patch.object(
|
|
||||||
yamale, "make_data", return_value=None
|
|
||||||
) as yamale_make_data_mock:
|
|
||||||
schema = yamale.make_schema(
|
|
||||||
content="""
|
|
||||||
name: str()
|
|
||||||
age: int(max=200)
|
|
||||||
height: num()
|
|
||||||
awesome: bool()
|
|
||||||
field: enum('option 1', 'option 2')
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make the `make_data` method return valid data.
|
|
||||||
yamale_make_data_mock.return_value = valid_data
|
|
||||||
|
|
||||||
# Should pass.
|
|
||||||
validate_all_data_set_descriptions(data_set_description_schema=schema)
|
|
||||||
|
|
||||||
# Make some of the data invalid.
|
|
||||||
yamale_make_data_mock.return_value = invalid_data_1
|
|
||||||
|
|
||||||
# Should fail because of the invalid field values.
|
|
||||||
with self.assertRaises(yamale.YamaleError) as context_manager:
|
|
||||||
validate_all_data_set_descriptions(data_set_description_schema=schema)
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
str(context_manager.exception),
|
|
||||||
"""Error validating data
|
|
||||||
age: 'asdf' is not a int.
|
|
||||||
awesome: 'asdf' is not a bool.""",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make some of the data missing.
|
|
||||||
yamale_make_data_mock.return_value = invalid_data_2
|
|
||||||
|
|
||||||
# Should fail because of the missing fields.
|
|
||||||
with self.assertRaises(yamale.YamaleError) as context_manager:
|
|
||||||
validate_all_data_set_descriptions(data_set_description_schema=schema)
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
str(context_manager.exception),
|
|
||||||
"""Error validating data
|
|
||||||
name: Required field missing""",
|
|
||||||
)
|
|
||||||
|
|
||||||
@mock.patch("builtins.open", new_callable=mock.mock_open)
|
|
||||||
def test_write_data_set_description_template_file(self, builtins_writelines_mock):
|
|
||||||
schema = yamale.make_schema(
|
|
||||||
content="""
|
|
||||||
name: str()
|
|
||||||
age: int(max=200)
|
|
||||||
height: num()
|
|
||||||
awesome: bool()
|
|
||||||
field: enum('option 1', 'option 2')
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
data_set_description_field_descriptions = {
|
|
||||||
"name": "The name of the thing.",
|
|
||||||
"age": "The age of the thing.",
|
|
||||||
"height": "The height of the thing.",
|
|
||||||
"awesome": "The awesome of the thing.",
|
|
||||||
"field": "The field of the thing.",
|
|
||||||
}
|
|
||||||
|
|
||||||
write_data_set_description_template_file(
|
|
||||||
data_set_description_schema=schema,
|
|
||||||
data_set_description_field_descriptions=data_set_description_field_descriptions,
|
|
||||||
template_file_path="mock_template.yaml",
|
|
||||||
)
|
|
||||||
|
|
||||||
call_to_writelines = builtins_writelines_mock.mock_calls[2][1][0]
|
|
||||||
|
|
||||||
self.assertListEqual(
|
|
||||||
call_to_writelines,
|
|
||||||
[
|
|
||||||
"# Note: This template is automatically generated by the function\n"
|
|
||||||
"# `write_data_set_description_template_file` from the schema\n"
|
|
||||||
"# and field descriptions files. Do not manually edit this file.\n\n",
|
|
||||||
"name: \n",
|
|
||||||
"# Description: The name of the thing.\n",
|
|
||||||
"# Required field: True\n",
|
|
||||||
"# Field type: str\n",
|
|
||||||
"\n",
|
|
||||||
"age: \n",
|
|
||||||
"# Description: The age of the thing.\n",
|
|
||||||
"# Required field: True\n",
|
|
||||||
"# Field type: int\n",
|
|
||||||
"\n",
|
|
||||||
"height: \n",
|
|
||||||
"# Description: The height of the thing.\n",
|
|
||||||
"# Required field: True\n",
|
|
||||||
"# Field type: num\n",
|
|
||||||
"\n",
|
|
||||||
"awesome: \n",
|
|
||||||
"# Description: The awesome of the thing.\n",
|
|
||||||
"# Required field: True\n",
|
|
||||||
"# Field type: bool\n",
|
|
||||||
"\n",
|
|
||||||
"field: \n",
|
|
||||||
"# Description: The field of the thing.\n",
|
|
||||||
"# Required field: True\n",
|
|
||||||
"# Field type: enum\n",
|
|
||||||
"# Valid choices are one of the following: ('option 1', 'option 2')\n",
|
|
||||||
"\n",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -1 +0,0 @@
|
||||||
yamale==3.0.6
|
|
|
@ -1,21 +0,0 @@
|
||||||
"""Setup script for `data_roadmap` package."""
|
|
||||||
import os
|
|
||||||
|
|
||||||
from setuptools import find_packages
|
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
# TODO: replace this with `poetry`. https://github.com/usds/justice40-tool/issues/57
|
|
||||||
_PACKAGE_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
|
|
||||||
with open(os.path.join(_PACKAGE_DIRECTORY, "requirements.txt")) as f:
|
|
||||||
requirements = f.readlines()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="data_roadmap",
|
|
||||||
description="Data roadmap package",
|
|
||||||
author="CEJST Development Team",
|
|
||||||
author_email="justice40open@usds.gov",
|
|
||||||
install_requires=requirements,
|
|
||||||
include_package_data=True,
|
|
||||||
packages=find_packages(),
|
|
||||||
)
|
|
|
@ -1,8 +0,0 @@
|
||||||
# Documentation
|
|
||||||
|
|
||||||
Here you'll find all Justice40 project documentation.
|
|
||||||
|
|
||||||
In particular:
|
|
||||||
|
|
||||||
- [Decisions](decisions) - Documentation of significant project decisions
|
|
||||||
- [Data Roadmap](data-roadmap.md) - a description of our process for adding new datasets to our backlog
|
|
|
@ -1,21 +0,0 @@
|
||||||
# Architecture
|
|
||||||
|
|
||||||
The below is a general architecture of our proposed system:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Updating the Diagram
|
|
||||||
|
|
||||||
**Note: Do Not directly modify the svg file, it is generated automatically!**
|
|
||||||
|
|
||||||
In the event that you are interested in updating the architecture of our system, please go through the Architecture Decision Record Process (see [here](https://github.com/usds/justice40-tool/tree/main/docs/decisions) for more detail on this process).
|
|
||||||
|
|
||||||
Provided you have already done this, however, and/or would like to make small changes to the diagram itself, please read on!
|
|
||||||
|
|
||||||
This diagram was generated from a text-based markdown-like file using [MermaidJS](https://mermaid-js.github.io/) syntax and the [Compile Mermaid Markdown](https://github.com/marketplace/actions/compile-mermaid-markdown) Github Action.
|
|
||||||
|
|
||||||
To update, consult Mermaid syntax [here](https://mermaid-js.github.io/mermaid/#/flowchart) and update the `architecture.mmd` file.
|
|
||||||
|
|
||||||
To preview your changes, see Use Cases and Integrations [here](https://mermaid-js.github.io/mermaid/#/integrations) in order to setup editor integration.
|
|
||||||
|
|
||||||
Once you are satisfied with your change, create a pull request. The `compile_mermaid.yml` workflow ([here](https://github.com/usds/justice40-tool/blob/main/.github/workflows/compile_mermaid.yml)) will automatically generate and commit an SVG version of your diagram with the name `X.svg`.
|
|
Before Width: | Height: | Size: 30 KiB |
|
@ -1,39 +0,0 @@
|
||||||
graph LR
|
|
||||||
subgraph c["Community"]
|
|
||||||
input["Community Input"]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph ds["Data Selection"]
|
|
||||||
input --> Intake
|
|
||||||
input --> Evolution
|
|
||||||
input --> Voting
|
|
||||||
Intake --> Evolution --> Voting
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph s["Geoplatform.gov"]
|
|
||||||
subgraph dp["Data Pipeline (Justice40 Repo)"]
|
|
||||||
Voting --> a["Approved Datasets"]
|
|
||||||
a --> Properties
|
|
||||||
a --> Geometries
|
|
||||||
Properties --> Processing
|
|
||||||
Geometries --> Processing
|
|
||||||
input --> Processing
|
|
||||||
end
|
|
||||||
subgraph Server
|
|
||||||
Processing --> GeoJSON
|
|
||||||
GeoJSON --> db[("Feature Database")]
|
|
||||||
db --> tileserv["Tile Server"]
|
|
||||||
end
|
|
||||||
subgraph j40["Justice40 Client"]
|
|
||||||
tileserv --> vl
|
|
||||||
ts["Tile Styling"] --> vl["Justice40 Visualization Library"]
|
|
||||||
vl --> fe["Justice40 Static Site Frontend"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph oc["Other Clients"]
|
|
||||||
tileserv -- API --> 3p["Third Party Apps"]
|
|
||||||
GeoJSON -- API --> 3p
|
|
||||||
db -- API --> 3p
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
# 1. Record architecture decisions
|
|
||||||
|
|
||||||
Date: 2021-04-21
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
Accepted
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
We need to record the architectural decisions made on this project.
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
|
|
|
@ -1,12 +0,0 @@
|
||||||
(From https://github.com/Malvoz/web-maps-wcag-evaluation/blob/master/README.md) 1.1.1 Non-text Content (A) Result 1.1.1 Non-text Content (A) Notes 1.3.1 Info and Relationships (A) Result 1.3.1 Info and Relationships (A) Notes 1.4.3 Contrast (Minimum) Result 1.4.3 Contrast (Minimum) Notes 2.1.1 Keyboard (A) Result 2.1.1 Keyboard (A) Notes 2.1.2 No Keyboard Trap (A) Result 2.1.4 Character Key Shortcuts (A) Result 2.1.4 Character Key Shortcuts (A) Notes 2.4.3 Focus Order (A) Result 2.4.3 Focus Order (A) Notes 2.4.7 Focus Visible (AA) Result 2.4.7 Focus Visible (AA) Notes 2.5.5 Target Size (AAA) Result 2.5.5 Target Size (AAA) Notes 3.1.2 Language of Parts (AA) Result 3.2.2 On Input (A) Result 3.2.5 Change on Request (AAA) Result 3.2.5 Change on Request (AAA) Notes 4.1.2 Name, Role, Value (A) Result 4.1.2 Name, Role, Value (A) Notes Success Count
|
|
||||||
Google Maps embed 0 The image depicting "Google" (logo) is neither hidden from ATs nor has a text alternative. 0 The zoom controls' disabled state cannot be programmatically determined. 0 The "View larger map" link has a contrast ratio of 3.79. Some map text (i.e. ocean labels) do not mean the minimum contrast ratio. 0 Map display is not pannable using a keyboard. Control to toggle between "Satellite imagery" and "Street map" is not keyboard operable. 1 N/A (No single key shortcuts available.) 0 ChromeVox (no announcement) "Terms of use link" "View larger map link" "Zoom in button" "Zoom out button" (no announcement) NVDA "Google Maps frame clickable" "Terms of use link" "clickable View larger map link" "Zoom in button Zoom in" "Zoom out button Zoom out" "clickable" 0 2 tab stops without focus indicators. 0 4/4 targets are too small. 0 1 0 Both the "View larger map" link and the "Terms of Use" link open in new windows without warning. The "View larger map" link is especially unexpected as stylistically, it resembles a button, not a link. 0 The "map component" (<div tabindex="0">, which acts as a control to zoom the map display) is missing name and role. Control to toggle between "Satellite imagery" and "Street map" is missing name and role. 2
|
|
||||||
Google Maps Platform API 0 The link that opens a new window to view the current location (in "Street view") is visually presented as an icon, but is missing a text alternative. 0 The web map's semantic structure as a distinct piece of content cannot be programmatically determined. The zoom controls' disabled state cannot be programmatically determined. The fullscreen control's pressed state cannot be programmatically determined. 0 The "View larger map" link has a contrast ratio of 3.79. Some map text (i.e. ocean labels) do not mean the minimum contrast ratio. 0 Control to initiate "Street view" (so called "Pegman") is only draggable using a mouse, and not keyboard operable. The "Labels" toggle is only accessible in a drop-down after hovering a mouse pointer over the "Satellite" button. The "Terrain" toggle is only accessible in a drop-down after hovering a mouse pointer over the "Map" button. Control to exit "Street view" (<div jsaction="closeControl.click">) is not keyboard operable. 1 1 0 ChromeVox (no announcement) "Open this area in Google Maps, opens a new window link" "Terms of use link" "Toggle fullscreen view button" "Zoom in button" "Zoom out button" "Show street map button pressed" "Show satellite imagery button not pressed" NVDA "clickable" "Open this area in Google Maps, opens a new window link" "Terms of use link" "Toggle fullscreen view button toggle fullscreen view" "Zoom in button zoom in" "Zoom out button zoom out" "clickable Show street map toggle button pressed show street map" "clickable Show satellite imagery toggle button not pressed show satellite imagery" 0 13 tab stops without focus indicators (in "Street view"). 0 14/14 targets are too small. 0 0 0 The "Terms of Use" link opens in new window without warning. The Google logo link contains the screen reader label "Open this area in Google Maps", which does give the user sufficient warning, as it may open in the native Maps app on mobile. 0 The "map component" (<div tabindex="0">, which acts as control to both zoom and pan the map display) is missing name and role. Control to exit "Street view" (<div jsaction="closeControl.click">) is missing name and role. Control to reset the bearing (in "Street view") is missing name. Control to rotate the bearing counter-clockwise (in "Street view") is missing name. Control to rotate the bearing clockwise (in "Street view") is missing name. 2
|
|
||||||
MapKit JS (Apple Maps) API 0 The control to change/reset the bearing has a child node with the text character "N" to convey "North", it is presumably decorative but is not hidden from ATs. 0 The web map's semantic structure as a distinct piece of content cannot be programmatically determined. 0 Some map labels (i.e. bodies of water, minor street labels) do not meet the minimum contrast ratio. 0 Map display is not pannable using a keyboard. 1 N/A (No single key shortcuts available.) 1 ChromeVox (no announcement) (no announcement) (no announcement) (no announcement) (no announcement) (no announcement) (The browser extension cannot read the Shadow DOM because shadowRoot.mode is closed.) NVDA "Change the map type button collapsed change the map type" "0 degrees 0 degrees slider N" "Zoom in button zoom in" "Zoom out button zoom out" "Legal button" 1 0 5/6 targets are too small. 0 1 0 The "Legal" link is incorrectly marked up as a button (role="button"), opens in new tab without warning. 0 Control to rotate/reset the bearing is missing role. (Note: there are two separate controls providing the same functionality, for clarification, this refers to <div tabindex="0"> and not the <input type="range" aria-label="{x} degrees"> control.) 4
|
|
||||||
TomTom Maps SDK for Web 0 Character "×" of the control to close the copyright message dialog is decorative but is not hidden from ATs. 0 The web map's semantic structure as a distinct piece of content cannot be programmatically determined. The zoom controls' disabled state cannot be programmatically determined. 0 Some map labels (i.e. buildings) do not meet the minimum contrast ratio. 0 Control to open the copyright message dialog is not keyboard accessible. Control to close the copyright message dialog is not keyboard accessible. 1 1 1 ChromeVox "Map zoom in zoom out copyright 1992 to 2020 TomTom" "Map" "zoom in button" "zoom out button" NVDA "clickable map graphic zoom in button zoom in zoom out button zoom out clickable copyright 1992 to 2020 TomTom" (no announcement) "Zoom in button zoom in" "Zoom out button zoom out" 0 2 tab stops without focus indicators. 0 3/3 targets are too small. 0 0 1 0 The "map component" (<div tabindex="0">, which acts as a control to both zoom and pan the map display) is missing name and role. Control to open the copyright message dialog is missing role. Control to close the copyright message dialog is missing name and role. 4
|
|
||||||
OpenLayers API 0 Characters "+" and "−" of the zoom controls are decorative but are not hidden from ATs. 0 The web map's semantic structure as a distinct piece of content cannot be programmatically determined. The zoom controls' disabled state cannot be programmatically determined. 0 Some map labels (i.e. regional/districts) do not meet the minimum contrast ratio (uses OpenStreetMap tiles). 1 1 0 Both the arrow keys (used to pan the map display) as well as the keyboard shortcuts + and - (to zoom) can be activated despite unrelated components having focus. 1 ChromeVox "Plus button" "Minus button" "OpenStreetMap link list item-list with 1 items" NVDA "clickable Plus button zoom in" "button Zoom out" "list with 1 items OpenStreetMap link" 1 0 2/2 targets are too small. 0 1 0 The "OpenStreetMap" link opens in a new tab without warning. 0 Controls to zoom in and zoom out do not have proper names (while the titles "Zoom in" and "Zoom out" are appropriate, the child text nodes "+" and "−" takes precedence in this case, according to the accessible name and description computation, and is announced as "button plus" and "button minus" in ChromeVox, for example). 5
|
|
||||||
Bing Maps Control API 1 0 The web map's semantic structure as a distinct piece of content cannot be programmatically determined. Scale bars/rulers are missing contextual information (alternatively should be hidden from ATs). The "Reset orientation" control's disabled state cannot be programmatically determined. The "Locate me" control's pressed state cannot be programmatically determined. 0 Some map labels (i.e. bodies of water, district labels) do not meet the minimum contrast ratio. 0 Control to toggle area labels under "Bird's Eye view" is not keyboard accessible (has tabindex="-1"). 1 1 1 ChromeVox "Bing Maps interact to see more" "menu collapsed" "Locate me button" "Current level 1 zoom in button" "Bing Maps link" "Terms link" NVDA "clickable Bing Maps interact to see more" "Current map type: Road, view Aerial, Bird's Eye and more menu" "Locate me button locate me" "Current level 1 zoom in button zoom in" "Bing Maps link" "Terms link terms" 1 0 9/14 targets are too small. 0 1 0 Both the Bing Maps logo and the "Terms" link open in new windows without warning. 0 The "map component" (<div tabindex="0">, which acts as a control to both zoom and pan the map display) is missing role. Control to toggle area labels under "Bird's Eye view" is missing name. 6
|
|
||||||
OpenStreetMap embed 1 0 The zoom controls' disabled state cannot be programmatically determined. 0 Some map labels (i.e. regional/districts) do not meet the minimum contrast ratio (uses OpenStreetMap tiles). 1 1 1 1 ChromeVox "Zoom in zoom out report a problem copyright OpenStreetMap contributors" "Zoom in button" "Zoom out button" "Report a problem" "OpenStreetMap" NVDA "OpenStreetMap frame clickable zoom in zoom out report a problem copyright OpenStreetMap contributors zoom in button zoom in zoom out button zoom out clickable report a problem link copyright OpenStreetMap link contributors" "Zoom in button zoom in" "Zoom out button zoom out" "clickable Report a problem link" "OpenStreetMap link" 0 1 tab stop without focus indicator. 4 tab stops where focus indicators aren't persistently visible. 0 2/2 targets are too small. 0 1 0 The "Report a problem" and "OpenStreetMap" links open in a new tab without warning. 0 The "map component" (<div tabindex="0">, which acts as a control to both zoom and pan the map display) is missing name and role. 6
|
|
||||||
MapBox GL JS API 1 0 The web map's semantic structure as a distinct piece of content cannot be programmatically determined. 1 0 Control to display attribution and feedback links is not keyboard accessible. [Github PR] 1 1 1 ChromeVox "Map" "MapBox logo link" NVDA "clickable map graphic" "MapBox logo link" 0 1 tab stop without focus indicator. [Github PR] 0 2/2 targets are too small. 0 1 0 Mapbox logo link opens in a new tab without warning. 0 The "map component" (<canvas tabindex="0">, which acts as a control to both zoom and pan the map display) is missing role. [Github PR] Control to display attribution and feedback links is missing name and role. [Github PR] 6
|
|
||||||
Leaflet JS API 1 0 The web map's semantic structure as a distinct piece of content cannot be programmatically determined. [Github issue] The zoom controls' disabled state cannot be programmatically determined. [Github issue] 0 Some map labels (i.e. regional/districts) do not meet the minimum contrast ratio (uses OpenStreetMap tiles). 1 1 1 1 ChromeVox "Zoom in zoom out Leaflet map data copyright OpenStreetMap contributors" "Zoom in button" "Zoom out button" "Leaflet link" "OpenStreetMap link" NVDA "clickable Zoom in zoom out Leaflet map data copyright OpenStreetMap contributors zoom in button zoom in zoom out button Zoom out clickable Leaflet link a JS library for interactive maps map data copyright OpenStreetMap link contributors" "Zoom in button zoom in" "Zoom out button zoom out" "clickable Leaflet link a JS library for interactive maps" "OpenStreetMap link" 0 1 tab stop without focus indicator. 4 tab stops where focus indicators aren't persistently visible. [Github issue] 0 2/2 targets are too small. 0 1 1 0 The "map component" (<div tabindex="0">, which acts as a control to both zoom and pan the map display) is missing name and role. [Github issue] 7
|
|
||||||
MapBox Studio embed 1 0 The "Zoom out" control's disabled state cannot be programmatically determined. [Github PR] 1 0 Control to display attribution and feedback links is not keyboard accessible. [Github PR] 1 1 1 ChromeVox "Map" "Search edit text" "Zoom in button" "Zoom out button" "Reset bearing to north button" "MapBox logo link" NVDA "MapBox Studio frame clickable map graphic" "Search edit blank" "Zoom in button zoom in" "Zoom out button zoom out" "Reset bearing to north button reset bearing to north" "MapBox logo link" 0 1 tab stop without focus indicator. [Github PR] 0 7/7 targets are too small. 1 1 0 Mapbox logo link opens in a new tab without warning. 0 The "map component" (<canvas tabindex="0">, which acts as a control to both zoom and pan the map display) is missing role. [Github PR] Control to display attribution and feedback links is missing name and role. [Github PR] 7
|
|
||||||
Bing Maps embed 1 0 Scale bars/rulers are missing contextual information (alternatively should be hidden from ATs). 0 Some map labels (i.e. bodies of water, district labels) do not meet the minimum contrast ratio. 1 1 1 1 ChromeVox "Bing Maps interact to see more" "Current level 1 zoom in button" "Bing Maps link" "Terms link" NVDA "Bing Maps frame clickable Bing Maps interact to see more" "Current level 1 zoom in button zoom in" "Bing Maps link" "Terms link terms" 1 0 3/3 targets are too small. 1 1 0 Both the Bing Maps logo and the "Terms" link open in new windows without warning. 0 The "map component" (<div tabindex="0">, which acts as a control to both zoom and pan the map display) is missing role. 8
|
|
Can't render this file because it contains an unexpected character in line 2 and column 41.
|
Before Width: | Height: | Size: 751 KiB |
Before Width: | Height: | Size: 422 KiB |
Before Width: | Height: | Size: 111 KiB |
Before Width: | Height: | Size: 340 KiB |
Before Width: | Height: | Size: 558 KiB |
Before Width: | Height: | Size: 738 KiB |
|
@ -1,5 +0,0 @@
|
||||||
,OpenLayers,OpenLayers+MB,OpenLayers+React,Leaflet,Mapbox,Mapbox React
|
|
||||||
Style Loaded,300.22,1195.81,331.85,4460.74,225.97,577.28
|
|
||||||
Map Idle,1039.56,1195.63,1184.71,126.85,3837.53,2146.08
|
|
||||||
DOM Interactive,126.88,137.59,193.75,131.33,154.01,262.01
|
|
||||||
DOM complete,132.70,138.13,193.89,240.13,176.06,353.36
|
|
|
Before Width: | Height: | Size: 1.1 MiB |
|
@ -1,5 +0,0 @@
|
||||||
,OpenLayers,OpenLayers+MB,OpenLayers+React,Leaflet,Mapbox,Mapbox React
|
|
||||||
Style Loaded,408.89,383.19,439.83,1782.45,147.41,0.00
|
|
||||||
Map Idle,207.23,1263.27,1318.54,120.00,1970.86,1659.42
|
|
||||||
DOM Interactive,146.15,108.78,171.48,124.38,143.74,269.40
|
|
||||||
DOM complete,146.45,109.33,171.69,160.65,166.53,374.69
|
|
|
Before Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 25 KiB |
|
@ -1,182 +0,0 @@
|
||||||
# Mapping Visualization Library
|
|
||||||
|
|
||||||
- Status: approved
|
|
||||||
- Deciders: Lucas Brown, Shelby Switzer, Nat Hillard
|
|
||||||
- Date: 2021-04-21
|
|
||||||
- Tags: frontend
|
|
||||||
|
|
||||||
Technical Story: [#7](https://github.com/usds/environmental-justice/issues/7)
|
|
||||||
|
|
||||||
## Context and Problem Statement
|
|
||||||
|
|
||||||
In order to visualize the metrics of the Justice 40 initiative, we need a client-side mapping solution. This solution should should reflect the values of our initiative, and it should reflect and respond to the diverse needs of our users.
|
|
||||||
|
|
||||||
**Team Values**: The solution should be open-source, modular, and adopt modern best practices.
|
|
||||||
|
|
||||||
**User needs**: The solution should be performant, accessible, internationalized/localized, and minimize data usage.
|
|
||||||
|
|
||||||
We provide more detail on these factors below.
|
|
||||||
|
|
||||||
## Decision Drivers
|
|
||||||
|
|
||||||
### Team Values
|
|
||||||
|
|
||||||
- **Open source** : Our technology choices should "default to open" as detailed in the [Digital Services Playbook](https://playbook.cio.gov/#play13), in order to encourage community contribution, minimize vendor lock-in, and lower the barrier to entry to participate in the development of the tool. Ideally such a solution should be be true [Free and Open Source Software](https://en.wikipedia.org/wiki/Free_and_open-source_software), licensed for use, copy, and modification as well as source code-visible.
|
|
||||||
- **Ease of implementation** : In the name of welcoming a large body of open-source contributors, our implementation should not require too significant insider knowledge of mapping or GIS systems. At the same time, more advanced functionality should be familiar to those who do have such backgrounds, to enable them to contribute expertise where warranted. Additionally, it should be possible to get an environment up and running locally without a necessary API key or other paid plan.
|
|
||||||
- **Modular** : we should minimize vendor lock-in and allow for mixing and matching solutions from different providers -- from server-vended tiling solutions to tile storage and basemaps -- in order to optimize user experience and find the best tool for the job at all layers of the stack.
|
|
||||||
- **Modern** : the library should allow for its work to be done entirely in the browser; it should provide the ability to load on demand (in binary, protobuf [format](https://github.com/mapbox/vector-tile-spec)) and visualize so-called [vector tiles](https://en.wikipedia.org/wiki/Vector_tiles) to make use of the rendering power of [WebGL](https://www.khronos.org/webgl/); and it should provide for client-side dynamic map styling according to relevant [standards](https://docs.mapbox.com/mapbox-gl-js/style-spec/) as opposed to just server-side pre-rendering of raster tiles.
|
|
||||||
|
|
||||||
### User Needs
|
|
||||||
|
|
||||||
- **Performant** : Library should be performant on a range of device types, enabling fast load times and ease of navigation in a highly-complex rendering environment where there may be hundreds of thousands of features.
|
|
||||||
- **Accessible** : the implementation we build with this library must be compliant according to [section 508](https://www.section508.gov/) of the Rehabilitation Act [(29 U.S.C. § 794d)](https://www.govinfo.gov/content/pkg/USCODE-2011-title29/html/USCODE-2011-title29-chap16-subchapV-sec794d.htm), as well as [Section 501](https://www.eeoc.gov/statutes/rehabilitation-act-1973) guidance regarding Reasonable Accommodation per US federal government legal requirements. Ideally it should adopt [Web Content Accessibility Guidelines](https://www.w3.org/WAI/standards-guidelines/wcag/) to at least the AA level. The library our solution builds upon should provide some of this out of the box.
|
|
||||||
- **Internationalized/Localized** : Nearly [22%](https://data.census.gov/cedsci/table?q=B16001&hidePreview=false&tid=ACSDT1Y2017.B16001&vintage=2018) of the US population speaks a language other than English at home. Our mapping solution should be familiar and easy to use by this population.
|
|
||||||
- **Minimizes network usage** : Where possible, we should seek to minimize network usage to ensure lower data usage costs and faster load times.
|
|
||||||
|
|
||||||
## Considered Options
|
|
||||||
|
|
||||||
1. [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) - v2.2.0 (3/25/21)
|
|
||||||
2. [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js) - v1.14.0 (3/24/21)
|
|
||||||
3. [Leaflet](https://github.com/Leaflet/Leaflet) - v1.7.1 (9/4/20) + [Leaflet.VectorGrid](https://github.com/Leaflet/Leaflet.VectorGrid) - v1.3.0 (2017)
|
|
||||||
4. [OpenLayers](https://github.com/openlayers/openlayers) - v6.5.0 (12/27/20)
|
|
||||||
5. [ArcGIS API for Javascript](https://developers.arcgis.com/javascript/latest/) - v4.18 (12/2020)
|
|
||||||
|
|
||||||
## Not Considered Options
|
|
||||||
|
|
||||||
- Non-ArcGIS-JS-API Esri [products](https://www.esri.com/en-us/arcgis/products/index) - The focus of this investigation is web APIs that can be incorporated into a website and rendered as part of a cloud-based, modular web-development flow. Additionally, these are not open source.
|
|
||||||
- [Google Maps Platform](https://developers.google.com/maps)/[Bing Maps APIs](https://www.microsoft.com/en-us/maps/choose-your-bing-maps-api) - These mapping are also closed source and proprietary, and do not allow for significant customization or visualization of the sort we would need for this product
|
|
||||||
- [d3.js](https://d3js.org/) - though impressive in its offerings and performance, d3 appears more oriented to more general-purpose infographic use than a mapping-specific product requiring significant GIS-adjacent features. We may consider aspects of D3 for particular chart renderings.
|
|
||||||
|
|
||||||
## Decision Outcome
|
|
||||||
|
|
||||||
**OpenLayers** ([Source](https://github.com/openlayers/openlayers) - v6.5.0 (12/27/20)). This library is completely free and open-source, modular, and modern. As a modern, well-typed and well-documented javascript library, it is approachable, yet offers a full-featured and familiar interface as well for those with a background in GIS systems. As it is free to use, anyone could clone our source and run our project locally without payment or registration. Additionally, it has relatively good performance for large feature sets, despite not using WebGL for rendering.
|
|
||||||
|
|
||||||
### Positive Consequences
|
|
||||||
|
|
||||||
- **Licensing** : OpenLayers is BSD 2-Clause [licensed](https://github.com/openlayers/openlayers/blob/main/LICENSE.md)
|
|
||||||
- **More fully featured** : OpenLayers has a number of GIS features that other web-based tools do not. A somewhat older [analysis](https://link.springer.com/article/10.1007/s10109-017-0248-z) done in 2017 concluded that when compared to a set of considered features, OpenLayers 3 had a relatively large number of supported overall GIS features:
|
|
||||||

|
|
||||||
- **Popularity** : OpenLayers is second only to Leaflet in the number of Github [stars](https://github.com/openlayers/openlayers/stargazers) it has received, close to 8000
|
|
||||||
- **Performance** :
|
|
||||||
- The below chart comes from an September 2020 [study](https://doi.org/10.3390/ijgi9100563). The purpose of this study was to compare OpenLayers to Mapbox-GL-JS and Leaflet (both raster and vector tile variants) as the potential basis for a Life Quality Index for 55,000+ census radius jurisdictions in Argentina.
|
|
||||||
 ([Source](https://doi.org/10.3390/ijgi9100563))
|
|
||||||
In this chart, the two letters following the library name are for basemap layer and feature layer. Further, "R" is "raster" and "V" is vector, and lower numbers indicate faster load times. "OpenLayersRR" and "OpenLayersRV", (signifying a raster base layer and vector feature layer), performed quite well across all device types compared to other libraries.
|
|
||||||
- We also performed local testing using puppeteer and web performance APIs, tested against a choropleth map of the cenus block groups, which represents a likely usecase for us. The results were as follows:
|
|
||||||
 ([Source](./0002-files/Maryland.csv))
|
|
||||||
Testing and results can be found on the `client_perf` [branch](https://github.com/usds/justice40-tool/tree/client_perf) in this repository.
|
|
||||||
- Finally, we tested these mapping solutions against a large dataset with many features, a map of national highways, at a lower zoom level. The tiles were still loaded on-demand, but the greater detail and number of features meant a more difficult render. Trends seemed similar to the above, with the exception of Leaflet:
|
|
||||||
 ([Source](./0002-files/Highways.csv))
|
|
||||||
Notes:
|
|
||||||
- OL+MB did not file the `tiledidload` event and thus there was not a separate measure for style loaded
|
|
||||||
- Apparent performance was different from measured/reported overall, particularly when it comes to zoom performance. This is an area to dig into further and measured at a later time to understand better.
|
|
||||||
- **Data Usage** : The same study above also analyzed the amount of data usage for each of the libraries under investigation, and the result was the below chart (Lower values are better). OpenLayers overall performed quite well
|
|
||||||
 ([Source](https://doi.org/10.3390/ijgi9100563))
|
|
||||||
|
|
||||||
### Negative Consequences
|
|
||||||
|
|
||||||
- **Learning Curve** : A [recent survey](https://pea.lib.pte.hu/bitstream/handle/pea/23611/farkas-gabor-phd-2020.pdf?sequence=1) ranking various mapping libraries ranked OpenLayers as "Hard" in a scale from "Basic" to "Very Hard", and furthermore calculated "Approximate Learning Curve for Javascript", by which it was on the harder end as well (more detail in paper):
|
|
||||||
 ([Source](https://pea.lib.pte.hu/bitstream/handle/pea/23611/farkas-gabor-phd-2020.pdf?sequence=1))
|
|
||||||
This is part of the tradeoff in the library's large feature set. Anecdotally, however, for the purpose of a simple choropleth map, implementation felt similar to other libraries.
|
|
||||||
- **Modern Stack** : OpenLayers requires a [plugin](https://github.com/openlayers/ol-mapbox-style) for the combination of MVT vector tiles and Mapbox GL Styles, though it does support it.
|
|
||||||
- **Rendering** : According to [geoapify](https://www.geoapify.com/mapbox-gl-new-license-and-6-free-alternatives), it has 'slightly less "polished" rendering quality and performance' compared to Mapbox/MapLibre GL. This is partly related to the fact that it does not yet support using WebGL for rendering anything more than points ([source](https://openlayers.org/workshop/en/webgl/points.html)).
|
|
||||||
|
|
||||||
## Observations
|
|
||||||
|
|
||||||
- **Accessibility**
|
|
||||||
- Accessibility for web mapping technology in general is [not](https://wcag-maps.nicchan.me/) [good](https://sparkgeo.com/blog/the-accessibility-of-web-maps/). As of now, the default 2D visualizations in and of themselves do not provide explicit 508 compliance, and there is no official accessibilty [statement](https://www.w3.org/WAI/planning/statements/) for OpenLayers.
|
|
||||||
- According to a recent exhaustive WCAG 2.1 [evaluation](https://github.com/Malvoz/web-maps-wcag-evaluation/) that compared several map providers, though almost all web maps performed [poorly](https://wcag-maps.nicchan.me/), OpenLayers came out in the middle of the pack:
|
|
||||||
 (Source: original [here](./0002-files/AccessibilityComparison.tsv), based on data [here](https://github.com/Malvoz/web-maps-wcag-evaluation/))
|
|
||||||
This chart counts the number of successful WCAG 2.1 criteria from the above-linked study.
|
|
||||||
- Mitigations:
|
|
||||||
- According to at least one accessibility [expert](http://stackoverflow.com/questions/15659051/google-maps-508-accessibility-without-styles/16060809#16060809), it is sufficient for compliance reasons to provide an alternative means of viewing the same information provided by the map, perhaps in a table or other format. This is a common solution to this problem.
|
|
||||||
- Though the library itself does not provide an accessibility guarantee, many of the 508 and WCAG standards are a matter of how you _use_ a particular library - e.g. ensuring proper color contrast, providing alternative markers not based on coloration, and providing text highlighting. We can guarantee that these are in place and ensure an accessibility audit of our featureset before release alongside user testing with low-vision and visually-impaired users directly.
|
|
||||||
|
|
||||||
## Pros and Cons of the Other Options
|
|
||||||
|
|
||||||
### Mapbox-GL JS
|
|
||||||
|
|
||||||
[Source](https://docs.mapbox.com/mapbox-gl-js/api/) - v2.2.0 (3/29/21)
|
|
||||||
|
|
||||||
#### Mapbox-GL JS Pros
|
|
||||||
|
|
||||||
- **Performance** : As can be seen in the above charts, Mapbox performed quite well in performance tests, in some instances performing the best of the examined options.
|
|
||||||
- **Data Usage**: In the data usage chart above, MapboxRV and MapboxVV also do quite well in the category of data usage.
|
|
||||||
- **Modern stack**: Mapbox supports WebGL rendering, vector tiles, and standardized tile styling
|
|
||||||
- **Modularity**: Mapbox interoperates with various basemap providers, and thanks to the open-sourcing of both its [vector tile format](https://docs.mapbox.com/vector-tiles/specification/) and its [styling language](https://docs.mapbox.com/mapbox-gl-js/style-spec/), there are a [number](https://github.com/mapbox/awesome-vector-tiles) of tools, libraries, and frameworks that easily interoperate with this solution.]
|
|
||||||
- **(Relative) Accessibility** : Mapbox-GL-JS came out 4th out of 12 in the above accessibility analysis. Mapbox also released several [patches](https://github.com/mapbox/mapbox-gl-js/pulls?q=is%3Apr++accessibility+) recently to address the issues that were found and are actively working on this.
|
|
||||||
- **Localization** : Through the mapbox-gl-language [plugin](https://github.com/mapbox/mapbox-gl-language/), it is possible to dynamically change the language of mapbox-provided basemaps to other languages. Additionally, they have a plugin for displaying [RTL languages](https://github.com/mapbox/mapbox-gl-rtl-text), and the [ability](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#types-number-format) to format numbers according to locale conventions.
|
|
||||||
- **Popularity** : According to [NPM Trends](https://www.npmtrends.com/mapbox-gl-vs-leaflet-vs-ol-vs-arcgis-js-api-vs-maplibre-gl) Mapbox-GL is the most downloaded package among those considered. More info [here](https://www.geoapify.com/map-libraries-comparison-leaflet-vs-mapbox-gl-vs-openlayers-trends-and-statistics)
|
|
||||||

|
|
||||||
|
|
||||||
#### Mapbox-GL JS Cons
|
|
||||||
|
|
||||||
- **Licensing** : Mapbox's December 2020 [announcement](https://github.com/mapbox/mapbox-gl-js/releases/tag/v2.0.0) of version 2.0 of their software changed ther license to proprietary and changed their pricing scheme to cover tiles loaded from outside of their service. This decision was met with [some criticism](https://joemorrison.medium.com/death-of-an-open-source-business-model-62bc227a7e9b) in the open-source mapping community.
|
|
||||||
- **Library Size** : Mapbox-GL is one of the larger libraries considered as far as filesize, coming in at 214.1 KB ([Source](https://www.npmtrends.com/mapbox-gl-vs-leaflet-vs-ol-vs-arcgis-js-api-vs-maplibre-gl))
|
|
||||||
- **Pricing** : As of version 2.0.0, Mapbox [charges](https://www.mapbox.com/pricing/#maploads) per "map load", defined as "every time Mapbox GL JS initializes on a webpage or in a web app". Up to 50,000 loads per month are free, and beyond this pricing varies. Users are likely to do a number of loads per sitting, however, so this is something we will need to consider, even in a relatively low-traffic tool.
|
|
||||||
|
|
||||||
### MapLibre-GL-JS
|
|
||||||
|
|
||||||
[Source](https://github.com/maplibre/maplibre-gl-js) - v1.14.0 (3/24/21)
|
|
||||||
MapLibre is a fork of Mapbox 1.13.0 that preserves the older BSD 3-clause license.
|
|
||||||
|
|
||||||
#### MapLibre-GL-JS Pros
|
|
||||||
|
|
||||||
- **Everything above** : Being a fork of Mapbox GL JS (though an earlier version), MapLibre has all of the benefits of Mapbox called out above
|
|
||||||
- **Licensing** : Returns to 3-Clause BSD [License](https://github.com/maplibre/maplibre-gl-js/blob/main/LICENSE.txt) means true FOSS licensing, as well as free operation
|
|
||||||
|
|
||||||
#### MapLibre-GL-JS Cons
|
|
||||||
|
|
||||||
- **Maturity** : This library is still quite young, having been formed only mid-December last year
|
|
||||||
- **Licensing Complications** : There may be licensing or legal challenges from Mapbox around the use of this tool
|
|
||||||
- **Component-based Wrappers** : Though supported by [react-map-gl](https://github.com/visgl/react-map-gl), there is not yet as full-featured support for MapLibre-GL in other component-based wrapper libraries
|
|
||||||
- **Backwards Compatibility** : There is an [issue](https://github.com/maplibre/maplibre-gl-js/pull/21) open now discussing the possibility of breaking backwards compatibility as part of a MapLibre 2.0 release. In practice this would mean we would not be able to use layers created within Mapbox in MapLibre tooling.
|
|
||||||
- **New Features** : There is not yet clarity on how and when this library would be updated and what its upcoming feature roadmap looks like
|
|
||||||
|
|
||||||
### Leaflet
|
|
||||||
|
|
||||||
[Source](https://github.com/Leaflet/Leaflet) - v1.7.1 (9/4/20) + [Leaflet.VectorGrid](https://github.com/Leaflet/Leaflet.VectorGrid) - v1.3.0 (2017)
|
|
||||||
Leaflet is a popular open-source mapping visualization library that has been around since 2010.
|
|
||||||
|
|
||||||
#### Leaflet Pros
|
|
||||||
|
|
||||||
- **Popularity** : Leaflet has by far the largest number of [stars](https://github.com/Leaflet/Leaflet/stargazers) on Gitbub (30,500 at the time of writing)
|
|
||||||
- **Modularity** : Leaflet has a [number](https://leafletjs.com/plugins.html) of plugins that augment its functionality, including [Leaflet.VectorGrid](https://github.com/Leaflet/Leaflet.VectorGrid) which enables Vector Tiles
|
|
||||||
- **License** : Leaflet is BSD 2-Clause [licensed](https://github.com/Leaflet/Leaflet/blob/master/LICENSE)
|
|
||||||
|
|
||||||
#### Leaflet Cons
|
|
||||||
|
|
||||||
- **Performance** : As seen above, Leaflet performance is quite poor across device types, relative to other libraries, even with the vector plugin
|
|
||||||
- **Modern Stack** : The lack of out-of-the-box vector tile support and the necessity of another library opens the possibility for version requirement mismatches and security vulnerabilities. Additionally, the last update to the VectorGrid plugin was a [year ago](https://github.com/Leaflet/Leaflet.VectorGrid/commits/master).
|
|
||||||
|
|
||||||
### ArcGIS API for Javascript
|
|
||||||
|
|
||||||
[Source](https://developers.arcgis.com/javascript/latest/) - v4.18 (12/2020)
|
|
||||||
|
|
||||||
#### ArcGIS API for Javascript Pros
|
|
||||||
|
|
||||||
- **Accessibility** : ArcGIS provides regularly-updated accessibility [conformance reports](https://www.esri.com/en-us/legal/accessibility/conformance-reports) detailing their products' conformance with 508 standards.
|
|
||||||
- Note: These audits are not all up to date - the ArcGIS online evaluation was last completed in September 2018
|
|
||||||
- Anecdotally, screen readers still struggle to read content without annotations - the onus is still on the map creator to ensure proper compliance.
|
|
||||||
- **Internationalization/Localization** : the API has its own `intl` module, described [here](https://developers.arcgis.com/javascript/latest/api-reference/esri-intl.html)
|
|
||||||
- Note: The default ES6 `Intl` module appears to do all of the things described above without need for further modificaiton
|
|
||||||
- **Name recognition** : Esri in general holds "approximately 43% of the global market share and estimated annual revenues of approximately $1.1 Billion, from roughly 300,000 customers" ([source](https://digital.hbs.edu/platform-digit/submission/esri-and-arcgis/])). Relatedly, Esri tools interoperate well with other Esri tools, and their ecosystem is large.
|
|
||||||
- Note: according to [one analysis](https://www.datanyze.com/market-share/mapping-and-gis--121) specifically the ArcGIS Web API holds only 0.48% of the marketshare overall.
|
|
||||||
- **Modularity** : As seen in ArcGIS [documentation](https://developers.arcgis.com/documentation/), Esri interoperates with Leaflet (through a custom fork), Mapbox GL JS, and OpenLayers.
|
|
||||||
|
|
||||||
#### ArcGIS API for Javascript Cons
|
|
||||||
|
|
||||||
- **License** : Most importantly, though Esri maintains a [handful](https://www.esri.com/en-us/arcgis/open-vision/initiatives/open-source) of open-source tools, ArcGIS API for JS itself, as well as many other tools within the Esri / ArcGIS portfolio, is [closed source](https://github.com/Esri/arcgis-js-api/blob/master/copyright.txt) and proprietary, hosting only minified versions of their software and not encouraging community contribution or feedback beyond forum contributions.
|
|
||||||
- **Complexity** : The feature set comes with the tradeoff of greater complexity in implementation for ArcGIS as well. Additionally, many more advanced features of the ArcGIS API require you to utilize the Dojo framework and/or Dijit components (see examples [here](https://developers.arcgis.com/javascript/latest/sample-code/widgets-frameworks-react/)), a requirement not held by other frameworks.
|
|
||||||
- **Modern** : ArcGIS-JS-API only recently got support for ES Modules in version 4.19 ([source](https://developers.arcgis.com/javascript/latest/release-notes/)). Additionally, many examples from earlier versions are still common, including in the ArcGIS API for JS documentation [itself](https://developers.arcgis.com/javascript/latest/get-started/), which still uses HTML includes. There are recently some newer [examples](https://github.com/Esri/jsapi-resources/tree/master/esm-samples) in github, but they also seem to have been recently added.
|
|
||||||
|
|
||||||
## Appendix A: Other comparisons
|
|
||||||
|
|
||||||
There are a handful of other similar comparisons out there, here are some references:
|
|
||||||
|
|
||||||
- Geoapify comparison ([Source](https://www.geoapify.com/mapbox-gl-new-license-and-6-free-alternatives))
|
|
||||||

|
|
||||||
- Flatlogic [Comparison](https://flatlogic.com/blog/top-mapping-and-maps-api/)
|
|
||||||
|
|
||||||
## Appendix B: Further Reading
|
|
||||||
|
|
||||||
- [Malvoz's WCAG 2.1 Summary](https://github.com/Malvoz/web-maps-wcag-evaluation/blob/master/README.md)
|
|
|
@ -1,105 +0,0 @@
|
||||||
# ADR Process
|
|
||||||
|
|
||||||
- Status: Proposed
|
|
||||||
- Deciders: Justice40 Team
|
|
||||||
- Date: 2021-05-10
|
|
||||||
- Tags: Process
|
|
||||||
|
|
||||||
Technical Story: N/A
|
|
||||||
|
|
||||||
## Context and Problem Statement
|
|
||||||
|
|
||||||
We should document the process of creating an ADR to provide clarity to committers of this project.
|
|
||||||
|
|
||||||
## Decision Drivers
|
|
||||||
|
|
||||||
- **Clarity** - Identify historical reasoning behind decision-making
|
|
||||||
- **Transparency** - Make decisions in the open
|
|
||||||
- **Currency** - ADRs reflect the current state of the world, but can be superseded by later decisions. The highest-numbered ADR reflects the most recent decision around a given topic.
|
|
||||||
- **Expediency** - We need good decisions, but good good software ships frequently. To balance these two factors, the ADR ensures we move forward in an informed way, but provides a way to self-correct as the world changes.
|
|
||||||
|
|
||||||
## Considered Options
|
|
||||||
|
|
||||||
- ADRs
|
|
||||||
- Informal Conversations
|
|
||||||
- Unilateral decision-making
|
|
||||||
|
|
||||||
## Decision Outcome
|
|
||||||
|
|
||||||
See Below for a way to use ADRs to make decisions on the project.
|
|
||||||
|
|
||||||
### What is an ADR?
|
|
||||||
|
|
||||||
An Architecture Decision Record is a text file, with a particular format, and an associated discussion. It is numbered for easy reference, and it has a general format.
|
|
||||||
|
|
||||||
### When Should I Create an ADR?
|
|
||||||
|
|
||||||
As outlined by Michael Nygard, ADRs represent "architecturally significant" decisions.
|
|
||||||
|
|
||||||
The subject of an ADR should affect one of the following areas:
|
|
||||||
|
|
||||||
- Structure
|
|
||||||
- Dependencies
|
|
||||||
- Interfaces
|
|
||||||
- Construction techniques
|
|
||||||
|
|
||||||
Small changes don't need one, but for larger changes that would introduce a new dependency or affect our overall system architecture, we recommend opening one.
|
|
||||||
|
|
||||||
### Process
|
|
||||||
|
|
||||||
Please see the below diagram:
|
|
||||||

|
|
||||||
|
|
||||||
1. **Start**: To create a new ADR, copy `template.md` into a new file in this directory, named `XXXX-name-of-issue.md` where the `XXXX` part is incremented from the previous ADR.
|
|
||||||
|
|
||||||
2. **Draft**: While your ADR is still being written, set the status to `draft`
|
|
||||||
|
|
||||||
3. **Proposed**: Fill out all fields, set status to `proposed`, and open a pull-request.
|
|
||||||
|
|
||||||
1. We recommend [Mermaid](https://mermaid-js.github.io) for diagrams! Create a file with a `.mmd` extension and reference `filename-mmd.svg` in your docs, and Github Actions will automatically create the svg fore you! Preview diagrams with the Mermaid [Live Editor](https://mermaid-js.github.io/mermaid-live-editor/).
|
|
||||||
2. To keep things organized, create a folder with the name `XXXX-files` and put supporting ADR material there.
|
|
||||||
|
|
||||||
4. **Email** Send an email to [the justice40-open-source Google Group](mailto:justice40-open-source@googlegroups.com) and link to the Pull Request created in (2). Set a deadline to solicit feedback, at most one week away.
|
|
||||||
|
|
||||||
5. **Consensus** In the event of consensus within the expressed timeline, feel free to merge.
|
|
||||||
|
|
||||||
6. **Concerns** If there are concerns raised, ensure that those that disagree understand the reasoning behind the decision and address concerns with specific mitigation steps. Request confirmation that concerns have been addressed. If so, merge!
|
|
||||||
|
|
||||||
7. **Discuss** In the event that disagreement persists, discuss the decision with the group in the next biweekly sync. Take a vote on next steps (opt-outs are OK!), and move forward.
|
|
||||||
|
|
||||||
8. **Merge**
|
|
||||||
|
|
||||||
1. If the proposal is **approved** by this vote, set its status to `approved`, and merge the change.
|
|
||||||
2. If it is **rejected**, set its status as `rejected`, and merge anyway for future reference!
|
|
||||||
|
|
||||||
9. **Update Other Issues** If people agree in discussion that this issue supersedes another, set the status of the other issue to `superseded`
|
|
||||||
|
|
||||||
10. **Close** Close the issue
|
|
||||||
|
|
||||||
### Positive Consequences
|
|
||||||
|
|
||||||
- Clarity on decisions
|
|
||||||
- A history of all decisions made on the project
|
|
||||||
- Inclusion of community voices in discussion of important topics
|
|
||||||
|
|
||||||
### Negative Consequences
|
|
||||||
|
|
||||||
- Folks have to follow a more involved process
|
|
||||||
- Might slow things down in the event of major disagreements
|
|
||||||
|
|
||||||
## Pros and Cons of Other Options
|
|
||||||
|
|
||||||
### Informal Conversations
|
|
||||||
|
|
||||||
- Pros: Easier, Faster
|
|
||||||
- Cons: Can leave people out
|
|
||||||
|
|
||||||
### Unilateral Decision Making
|
|
||||||
|
|
||||||
- Pros: The fastest option
|
|
||||||
- Cons: Many - less participation and inclusion, fewer perspectives.
|
|
||||||
|
|
||||||
## Links <!-- optional -->
|
|
||||||
|
|
||||||
- Documentation on the MADR [template](https://github.com/adr/madr)
|
|
||||||
- Blog [post](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions.html) introducing ADRs
|
|
Before Width: | Height: | Size: 17 KiB |
|
@ -1,12 +0,0 @@
|
||||||
graph TB
|
|
||||||
Draft -- ADR Pull Request Written --> Proposed
|
|
||||||
Proposed -- Emailed --> C{Consensus}
|
|
||||||
C --> |Yes| Merged
|
|
||||||
C --> |No| A{Concerns Addressed?}
|
|
||||||
A --> |Yes| Merged
|
|
||||||
A --> |No| Discussion
|
|
||||||
Discussion --> O{Voting Outcome}
|
|
||||||
O --> |Accepted| Merged
|
|
||||||
O --> |Rejected| Merged
|
|
||||||
Merged --> U[Update other Issues]
|
|
||||||
U-->Close
|
|
|
@ -1,142 +0,0 @@
|
||||||
# Client-side Framework
|
|
||||||
|
|
||||||
- Status: draft
|
|
||||||
- Deciders: Justice40 team
|
|
||||||
- Date: 2021-05-19
|
|
||||||
- Tags: front-end, client
|
|
||||||
|
|
||||||
Technical Story: https://github.com/usds/justice40-tool/issues/44
|
|
||||||
|
|
||||||
## Context and Problem Statement
|
|
||||||
|
|
||||||
We need to decide what framework to use for our front end client.
|
|
||||||
|
|
||||||
## Decision Drivers <!-- optional -->
|
|
||||||
|
|
||||||
- **Default to open** - Choose open source technology in our stack.
|
|
||||||
- **Team skills and capacity** - Most of the team has React experience.
|
|
||||||
- **Ease of implementation** - We're looking for significant community support and a shallow learning curve.
|
|
||||||
- **Skills of our community** - Our open source community as well as potential collaborators in federal government are likely to have experience with Python and JavaScript, so we're not strongly considering options outside of these ecosystems.
|
|
||||||
- **Can support following features:**
|
|
||||||
- USWDS integration
|
|
||||||
- Content management by nontechnical team member, such as with Markdown files or integration with a headless CMS
|
|
||||||
- Solid accessibility support
|
|
||||||
- Offline access / low bandwidth support
|
|
||||||
- Can generate static site
|
|
||||||
- Ease of use with Open Layers
|
|
||||||
- Localization support
|
|
||||||
|
|
||||||
## Considered Options
|
|
||||||
|
|
||||||
- Jekyll
|
|
||||||
- Gatsby
|
|
||||||
- Next.js
|
|
||||||
- Gridsome
|
|
||||||
- Eleventy
|
|
||||||
- Static HTML + JS site
|
|
||||||
- Create-react-app
|
|
||||||
|
|
||||||
## Decision Outcome
|
|
||||||
|
|
||||||
Chosen option: Gatsby, because it seems to hit the balance between being simple and static-site focused and having capabilities for scaling up features and eventually evolving to a full stack application should we need it.
|
|
||||||
|
|
||||||
### Positive Consequences <!-- optional -->
|
|
||||||
|
|
||||||
- Very large plugin [ecosystem](https://www.gatsbyjs.com/plugins)
|
|
||||||
- Batteries included philosophy helps in rendering static pages, routing, styling, markdown rendering
|
|
||||||
- Easy CMS support
|
|
||||||
- Straightforward to learn / pick up
|
|
||||||
- Fairly good [documentation](https://www.gatsbyjs.com/docs)
|
|
||||||
|
|
||||||
### Negative Consequences <!-- optional -->
|
|
||||||
|
|
||||||
- Test development environment ran out of memory. We debugged this further and could not replicate the problem on a new machine, but the original experience was annoying to work around and this [article](https://support.gatsbyjs.com/hc/en-us/articles/360053096273-Why-did-I-hit-Out-of-Memory-errors-) suggests it could be a more widespread problem.
|
|
||||||
- Local builds and refreshes feel slow compared to other frameworks on this list
|
|
||||||
- Seems a little more geared toward the blog usecase
|
|
||||||
|
|
||||||
## Pros and Cons of the Options
|
|
||||||
|
|
||||||
### Jekyll
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
|
|
||||||
- Easy to use
|
|
||||||
- Solid community support
|
|
||||||
- Team is familiar with it
|
|
||||||
- Can be hosted easily as static site on Github Pages or other platform
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
|
|
||||||
- Limited in terms of support for more advanced interactive features / JS components: based on team's experience, we will run up to the limitations pretty quickly
|
|
||||||
- Not scalable past a static site option if we need that
|
|
||||||
- We might need non-static solution in the future / dynamic pages - how
|
|
||||||
|
|
||||||
### Next
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
|
|
||||||
- By far the most commonly framework in this list - 1.4MM [total downloads](https://www.npmtrends.com/gatsby-vs-next-vs-nuxt-vs-vuepress-vs-create-react-app-vs-gridsome) as of May 2, 2021 . Gatsby, the second most-downloaded, has ~470,000
|
|
||||||
- Used by a number of well-known [companies and brands](https://nextjs.org/showcase)
|
|
||||||
- Flexible
|
|
||||||
- Mature tooling like `create-next-app`
|
|
||||||
- Straightforward, file-based routing
|
|
||||||
- Image optimization
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
|
|
||||||
- A bit awkward to have to opt out of SSR, navigate distinctions between static generation (with and without data), server-side rendering, and to know when to pre-render
|
|
||||||
- Compared to other frameworks here, fewer batteries included -- this is almost a pro but it means we'd have to reimplement things like markdown rendering
|
|
||||||
- Not having / wanting a server to work with, we're missing out on some of Next.js's functionality to start with
|
|
||||||
- A steeper learning curve than others on this list
|
|
||||||
|
|
||||||
### Gridsome
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
|
|
||||||
- Overall seems to share many of the benefits of Gatsby
|
|
||||||
- CMS integration
|
|
||||||
- GraphQL + data source integration very similar to Gatsby's
|
|
||||||
- Good [documentation](https://gridsome.org/docs/)
|
|
||||||
- Large [plugin](https://gridsome.org/plugins/) library
|
|
||||||
- Straightforward, file-structure-based routing
|
|
||||||
- Asset optimization, progressive image loading
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
|
|
||||||
- Vue support is limited to v2, no support yet for v3 (released Sept 2020) (though notably a lot of libraries don't support Vue3)
|
|
||||||
- Relatively unknown compared to other Vue-based frameworks: 7200 stars, compared to VuePress 18200, Nuxt.js's 33000
|
|
||||||
- There is a (really cool!) USWDS wrapper [library](https://github.com/usds/uswds-vue) -- and it's even supported by Mike Pritchard at USDS! -- providing Vue support, but it's still relatively new (Sep 2020)
|
|
||||||
|
|
||||||
### Eleventy
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
- Used elsewhere in USDS (PRIME ReportStream)
|
|
||||||
- Simple and straightforward
|
|
||||||
- Seems to be like Jekyll but for JavaScript
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
- Static-site only: Would be difficult to build out a more fully featured app later from this base
|
|
||||||
- Doesn't appear to offer us much in terms of speeding up the build
|
|
||||||
|
|
||||||
### Static HTML + JS site
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
|
|
||||||
- Simple: We wouldn't have to deal with idiosyncracies/conventions of a particular framework or work through dependency conflicts/issues
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
|
|
||||||
- We would have to build a lot more ourselves, including support for non-technical content management
|
|
||||||
- Not easily scalable past a static site (would have to do a lot of work to add a framework or functionality later)
|
|
||||||
|
|
||||||
### Create-react-app
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
- Standard app for getting started in React (and we are leaning towards React as a team)
|
|
||||||
- Like with the static site, there's less out of the box but more customization possible and potentially less to get in our way
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
- We'd have to figure out things like static site generation and CMS integration on our own
|
|
||||||
- Along those lines, doesn't appear to offer us much in terms of speeding up the build
|
|
||||||
|
|
||||||
## Links <!-- optional -->
|
|
|
@ -1,57 +0,0 @@
|
||||||
# Hosting
|
|
||||||
|
|
||||||
- Status: Proposed
|
|
||||||
- Deciders: Justice40 Team
|
|
||||||
- Date: 2021-05-18
|
|
||||||
- Tags: Hosting, Server, Geoplatform
|
|
||||||
|
|
||||||
Technical Story: https://github.com/usds/justice40-tool/issues/18 + https://github.com/usds/justice40-tool/issues/36
|
|
||||||
|
|
||||||
## Context and Problem Statement
|
|
||||||
We need a host for our data pipeline and front end website/app.
|
|
||||||
|
|
||||||
## Decision Drivers
|
|
||||||
|
|
||||||
- **Speed of launch** - We have a mandate to launch an initial version of our tool by July 27, and we want to get an informational site up much sooner.
|
|
||||||
- **Support for continuous integration and delivery** - We need our hosting provider to support a modern software development lifecycle that includes continuous integration and delivery, such as integration with our Github repository and CI/CD tool such as Github Actions.
|
|
||||||
- **Ease of implementation** - Ideally we can choose a platform that some of the team has experience with, that doesn't have a steep learning curve, and/or that has good support.
|
|
||||||
- **Commitment to open source and process** - Any code used for servers, data processing, or front end hosting must be able to be open source, so there cannot be limitations with regard to where/how code is hosted and shared.
|
|
||||||
|
|
||||||
|
|
||||||
## Considered Options
|
|
||||||
|
|
||||||
- Geoplatform
|
|
||||||
- Cloud.gov
|
|
||||||
|
|
||||||
## Decision Outcome
|
|
||||||
|
|
||||||
We will use Geoplatform.gov for hosting our data pipeline, tile server, and front end client. They have a set of shared service offerings that will enable us to have an open data pipeline from data source to tile API, enabling contributions to data processing at any point as well as enabling data access for users at any point (e.g. whether a user wants to access raw data or GeoJSON or tile format). See this  that illustrates the proposed system architecture and hosting.
|
|
||||||
|
|
||||||
### Positive Consequences
|
|
||||||
|
|
||||||
- No need to stand up our own servers for data processing or vending tiles
|
|
||||||
- No need to find separate static site hosting for our front end
|
|
||||||
- Help advance shared geo services for government
|
|
||||||
- Code for data transformations can live on an open Github repo and be collaborated on by the community
|
|
||||||
|
|
||||||
### Negative Consequences
|
|
||||||
|
|
||||||
- Possible delays or additional work: Some features and offerings we may want to use are still a work in progress on Geoplatform's side, so we may have to help build these out or wait for their release.
|
|
||||||
|
|
||||||
## Pros and Cons of Other Options
|
|
||||||
|
|
||||||
### Cloud.gov
|
|
||||||
|
|
||||||
Pros:
|
|
||||||
- Control over software development lifecycle including open source code and CI/CD setup
|
|
||||||
- Supports CI/CD
|
|
||||||
- Supports a shared service in government
|
|
||||||
- The team has experience with Cloud.gov
|
|
||||||
|
|
||||||
Cons:
|
|
||||||
- We would not be able to launch our website or backend on our expected timeline
|
|
||||||
- Would require a lot more build on our part for the data pipeline and tile server
|
|
||||||
- Would not have the flexibility to easily run serverless functions if we needed to
|
|
||||||
|
|
||||||
## Links <!-- optional -->
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Decisions
|
|
||||||
|
|
||||||
This folder is for gathering [Architectural Decision Records](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions.html), documenting decisions we have made for future reference. Read the above linked-to post to understand background and motivations, or read issue `0001-record-architecture-decisions.md`!
|
|
|
@ -1,73 +0,0 @@
|
||||||
# [short title of solved problem and solution]
|
|
||||||
|
|
||||||
- Status: [draft | proposed | rejected | accepted | deprecated | … | superseded by [xxx](yyyymmdd-xxx.md)] <!-- optional -->
|
|
||||||
- Deciders: [list everyone involved in the decision] <!-- optional -->
|
|
||||||
- Date: [YYYY-MM-DD when the decision was last updated] <!-- optional. To customize the ordering without relying on Git creation dates and filenames -->
|
|
||||||
- Tags: [space and/or comma separated list of tags] <!-- optional -->
|
|
||||||
|
|
||||||
Technical Story: [description | ticket/issue URL] <!-- optional -->
|
|
||||||
|
|
||||||
## Context and Problem Statement
|
|
||||||
|
|
||||||
[Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.]
|
|
||||||
|
|
||||||
## Decision Drivers <!-- optional -->
|
|
||||||
|
|
||||||
- [driver 1, e.g., a force, facing concern, …]
|
|
||||||
- [driver 2, e.g., a force, facing concern, …]
|
|
||||||
- … <!-- numbers of drivers can vary -->
|
|
||||||
|
|
||||||
## Considered Options
|
|
||||||
|
|
||||||
- [option 1]
|
|
||||||
- [option 2]
|
|
||||||
- [option 3]
|
|
||||||
- … <!-- numbers of options can vary -->
|
|
||||||
|
|
||||||
## Decision Outcome
|
|
||||||
|
|
||||||
Chosen option: "[option 1]", because [justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force force | … | comes out best (see below)].
|
|
||||||
|
|
||||||
### Positive Consequences <!-- optional -->
|
|
||||||
|
|
||||||
- [e.g., improvement of quality attribute satisfaction, follow-up decisions required, …]
|
|
||||||
- …
|
|
||||||
|
|
||||||
### Negative Consequences <!-- optional -->
|
|
||||||
|
|
||||||
- [e.g., compromising quality attribute, follow-up decisions required, …]
|
|
||||||
- …
|
|
||||||
|
|
||||||
## Pros and Cons of the Options <!-- optional -->
|
|
||||||
|
|
||||||
### [option 1]
|
|
||||||
|
|
||||||
[example | description | pointer to more information | …] <!-- optional -->
|
|
||||||
|
|
||||||
- Good, because [argument a]
|
|
||||||
- Good, because [argument b]
|
|
||||||
- Bad, because [argument c]
|
|
||||||
- … <!-- numbers of pros and cons can vary -->
|
|
||||||
|
|
||||||
### [option 2]
|
|
||||||
|
|
||||||
[example | description | pointer to more information | …] <!-- optional -->
|
|
||||||
|
|
||||||
- Good, because [argument a]
|
|
||||||
- Good, because [argument b]
|
|
||||||
- Bad, because [argument c]
|
|
||||||
- … <!-- numbers of pros and cons can vary -->
|
|
||||||
|
|
||||||
### [option 3]
|
|
||||||
|
|
||||||
[example | description | pointer to more information | …] <!-- optional -->
|
|
||||||
|
|
||||||
- Good, because [argument a]
|
|
||||||
- Good, because [argument b]
|
|
||||||
- Bad, because [argument c]
|
|
||||||
- … <!-- numbers of pros and cons can vary -->
|
|
||||||
|
|
||||||
## Links <!-- optional -->
|
|
||||||
|
|
||||||
- [Link type](link to adr) <!-- example: Refined by [xxx](yyyymmdd-xxx.md) -->
|
|
||||||
- … <!-- numbers of links can vary -->
|
|
1
en/404.html
Normal file
1
en/404/index.html
Normal file
1
en/index.html
Normal file
1
es/404.html
Normal file
1
es/404/index.html
Normal file
1
es/index.html
Normal file
1
framework-a3da6a89099751fcddf3.js
Normal file
1
index.html
Normal file
1
page-data/404.html/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-404-tsx","path":"/404.html","result":{"pageContext":{"language":"en","intl":{"language":"en","languages":["en","es"],"messages":{"71L0pp.defaultMessage":"Justice40","71L0pp.description":"Title of the project"},"routed":false,"originalPath":"/404/","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
page-data/404/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-404-tsx","path":"/404/","result":{"pageContext":{"language":"en","intl":{"language":"en","languages":["en","es"],"messages":{"71L0pp.defaultMessage":"Justice40","71L0pp.description":"Title of the project"},"routed":false,"originalPath":"/404/","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
page-data/app-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"webpackCompilationHash":"7c072212febea6e55278"}
|
1
page-data/en/404.html/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-404-tsx","path":"/en/404.html","result":{"pageContext":{"language":"en","intl":{"language":"en","languages":["en","es"],"messages":{"71L0pp.defaultMessage":"Justice40","71L0pp.description":"Title of the project"},"routed":true,"originalPath":"/404.html","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
page-data/en/404/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-404-tsx","path":"/en/404/","matchPath":"/en/*","result":{"pageContext":{"matchPath":"/en/*","language":"en","intl":{"language":"en","languages":["en","es"],"messages":{"71L0pp.defaultMessage":"Justice40","71L0pp.description":"Title of the project"},"routed":true,"originalPath":"/404/","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
page-data/en/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-index-tsx","path":"/en/","result":{"pageContext":{"language":"en","intl":{"language":"en","languages":["en","es"],"messages":{"71L0pp.defaultMessage":"Justice40","71L0pp.description":"Title of the project"},"routed":true,"originalPath":"/","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
page-data/es/404.html/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-404-tsx","path":"/es/404.html","result":{"pageContext":{"language":"es","intl":{"language":"es","languages":["en","es"],"messages":{"71L0pp":"Justicia40"},"routed":true,"originalPath":"/404.html","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
page-data/es/404/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-404-tsx","path":"/es/404/","matchPath":"/es/*","result":{"pageContext":{"matchPath":"/es/*","language":"es","intl":{"language":"es","languages":["en","es"],"messages":{"71L0pp":"Justicia40"},"routed":true,"originalPath":"/404/","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
page-data/es/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-index-tsx","path":"/es/","result":{"pageContext":{"language":"es","intl":{"language":"es","languages":["en","es"],"messages":{"71L0pp":"Justicia40"},"routed":true,"originalPath":"/","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
page-data/index/page-data.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"componentChunkName":"component---src-pages-index-tsx","path":"/","result":{"pageContext":{"language":"en","intl":{"language":"en","languages":["en","es"],"messages":{"71L0pp.defaultMessage":"Justice40","71L0pp.description":"Title of the project"},"routed":false,"originalPath":"/","redirect":true,"defaultLanguage":"en"}}},"staticQueryHashes":[]}
|
1
polyfill-aab98ca4729baf333612.js
Normal file
|
@ -1,15 +0,0 @@
|
||||||
# Tile Server
|
|
||||||
|
|
||||||
## What is it?
|
|
||||||
|
|
||||||
A simple tile server using [pg_tileserv](https://github.com/CrunchyData/pg_tileserv), based on pg_tileserv [docker example](https://github.com/CrunchyData/pg_tileserv/tree/master/examples/docker).
|
|
||||||
|
|
||||||
## How to use it?
|
|
||||||
|
|
||||||
1. Edit variables in `docker-compose.yml` if necessary to customize username/pw
|
|
||||||
2. Run `docker-compose up` to start running the server. It will likely stall in enabling extensions (TODO: figure this out))
|
|
||||||
3. Restart the server with ctrl-c. It should load the data from the `data/` directory exactly one time.
|
|
||||||
|
|
||||||
## Using
|
|
||||||
|
|
||||||
- Point your visualization library to the following URL, and select `vector` tiles: `http://localhost:7800/public.maryland/{z}/{x}/{y}.mvt`
|
|
|
@ -1,36 +0,0 @@
|
||||||
version: "3.9"
|
|
||||||
|
|
||||||
services:
|
|
||||||
tileserv:
|
|
||||||
image: pramsey/pg_tileserv:20210210
|
|
||||||
environment:
|
|
||||||
- DATABASE_URL=postgresql://map_dev_user:map_pwd@db/map_dev
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
ports:
|
|
||||||
- 7800:7800
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: kartoza/postgis:13-3.1
|
|
||||||
volumes:
|
|
||||||
- pgdata:/var/lib/postgresql/data
|
|
||||||
- ./data:/work
|
|
||||||
- ./load-data-db.sh:/docker-entrypoint-initdb.d/load_data-db.sh
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=map_dev_user
|
|
||||||
- POSTGRES_PASS=map_pwd
|
|
||||||
- POSTGRES_DB=map_dev
|
|
||||||
- ALLOW_IP_RANGE=0.0.0.0/0
|
|
||||||
ports:
|
|
||||||
- 5434:5432
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -h db -U map_dev_user -d map_dev"]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pgdata:
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# echo "[SQL INIT SCRIPT] Creating extension..."
|
|
||||||
# psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "gis" <<-EOSQL
|
|
||||||
# CREATE EXTENSION IF NOT EXISTS postgis;
|
|
||||||
# EOSQL
|
|
||||||
|
|
||||||
# Load Maryland geojson
|
|
||||||
echo "[SQL INIT SCRIPT] Loading data from geojson..."
|
|
||||||
ogr2ogr -progress -f PostgreSQL PG:"host=localhost dbname=map_dev user=map_dev_user password=map_pwd" /work/maryland.geojson -nln maryland
|
|
||||||
echo "Data load complete"
|
|