├── .husky ├── .gitignore └── pre-commit ├── .prettierrc.json ├── .vscode └── extensions.json ├── public ├── favicon.ico ├── index.html ├── data │ └── VSI_metadata.json ├── lang │ ├── en.json │ └── fr.json └── glue │ └── glue-icons.svg ├── .release-it.json ├── .prettierignore ├── .gitignore ├── tsconfig.json ├── license-report-template.html ├── src ├── main.ts ├── metadata.ts ├── case-studies.ts ├── global-vars.scss ├── stores.ts ├── zcta-county.ts ├── zip_data.ts ├── utils.ts ├── geo-utils.ts ├── TopQueries.svelte ├── App.svelte ├── Clusters.svelte ├── TrendsOverview.svelte ├── CountryPicker.svelte └── data.ts ├── .github └── workflows │ └── gh-pages.yml ├── license-report.js ├── package.json ├── CONTRIBUTING.md ├── rollup.config.js ├── makefile ├── README.md └── LICENSE /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["svelte.svelte-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-research/vaccination-search-insights/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "release": true 4 | }, 5 | "npm": { 6 | "publish": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | LICENSE 2 | node_modules 3 | package.json 4 | package-lock.json 5 | public 6 | README.md 7 | rollup.config.js 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | /public/data/ 4 | 5 | .DS_Store 6 | /public/data/Global_l0_vaccination_search_insights.csv 7 | /public/data/regions.csv 8 | /public/license_report.html -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | 4 | "include": ["src/**/*"], 5 | "exclude": ["node_modules/*", "__sapper__/*", "public/*"], 6 | "compilerOptions": { 7 | "resolveJsonModule": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /license-report-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Third Party Notices 8 | 9 | 19 | 20 | 21 |
22 | {{content}} 23 |
24 | 25 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import App from "./App.svelte"; 18 | 19 | const app = new App({ 20 | target: document.body, 21 | }); 22 | 23 | export default app; 24 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | schedule: 8 | # Weekly on Thursdays at 3pm PST (UTC-08). 9 | # Note: github runs the cron from the default branch, in our case "dev". 10 | - cron: '0 23 * * 4' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | deploy: 15 | runs-on: ubuntu-20.04 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | # Always deploy from the "main" branch. 20 | ref: main 21 | 22 | - name: Install 23 | run: npm ci 24 | 25 | - name: Build 26 | run: npm run build 27 | 28 | - name: Deploy 29 | uses: peaceiris/actions-gh-pages@v3 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | publish_dir: ./public 33 | -------------------------------------------------------------------------------- /license-report.js: -------------------------------------------------------------------------------- 1 | //Create a simple license report html page 2 | var checker = require("license-checker"); 3 | var fs = require("fs"); 4 | 5 | var template = fs.readFileSync("license-report-template.html").toString(); 6 | 7 | checker.init( 8 | { 9 | start: ".", 10 | }, 11 | function (err, packages) { 12 | if (err) { 13 | console.log(err); 14 | } else { 15 | var report = ""; 16 | for (package in packages) { 17 | let package_info = packages[package]; 18 | let licenseText = fs.readFileSync(package_info.licenseFile); 19 | let entry = 20 | `

${package}

` + 21 | (package_info.publisher 22 | ? `

Publisher: ${package_info.publisher}

` 23 | : "") + 24 | `

License: ${package_info.licenses}

` + 25 | `

License Text

` + 26 | `

${licenseText.toString()}

`; 27 | report += entry; 28 | } 29 | let output = template.replace("{{content}}", report); 30 | console.log(output); 31 | } 32 | } 33 | ); 34 | -------------------------------------------------------------------------------- /src/metadata.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* Simple component to return country metadata for a given country name. */ 18 | import metadata from '../public/data/VSI_metadata.json'; 19 | 20 | export function fetchCountryMetaData(countryNameString: string) { 21 | return metadata.filter(c => c.countryName === countryNameString); 22 | } 23 | 24 | export function fetchCountryNames() { 25 | // TODO: we can add some logic here and in the metadata.json to filter for a valid country tag. 26 | return metadata.map(({countryName}) => countryName) 27 | } 28 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | COVID-19 Vaccine Search Insights 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/case-studies.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Interface for a case study that contains information about the organization that wrote the study and the study itself. 19 | */ 20 | export interface CaseStudy { 21 | title: string; 22 | description: string; 23 | logoImgSrc: string; 24 | logoImgSrc2x: string; 25 | caseStudyHref: string; 26 | } 27 | 28 | /** 29 | * List of case studies that contains information about the organization that wrote the study and the study itself 30 | */ 31 | export const CASE_STUDIES: CaseStudy[] = [ 32 | { 33 | title: "pgp_title", 34 | description: "pgp_desc", 35 | logoImgSrc: 'https://gstatic.com/vsi/images/pgp-logo.png', 36 | logoImgSrc2x: 'https://gstatic.com/vsi/images/pgp-logo_2x.png', 37 | caseStudyHref: 'https://gstatic.com/vsi/pdf/pgp-case-study.pdf', 38 | }, 39 | { 40 | title: "grapevine_title", 41 | description: "grapevine_desc", 42 | logoImgSrc: 'https://gstatic.com/vsi/images/grapevine-health-logo.png', 43 | logoImgSrc2x: 'https://gstatic.com/vsi/images/grapevine-health-logo_2x.png', 44 | caseStudyHref: 'https://gstatic.com/vsi/pdf/grapevine-health-case-study.pdf', 45 | }, 46 | ]; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vaccination-search-insights", 3 | "version": "1.1.7", 4 | "scripts": { 5 | "build": "rollup -c", 6 | "dev": "rollup -c -w", 7 | "postinstall": "make data; node license-report.js > public/license_report.html", 8 | "start": "sirv public --no-clear", 9 | "validate": "svelte-check", 10 | "predeploy": "npm run build", 11 | "prepare": "husky install" 12 | }, 13 | "devDependencies": { 14 | "@rollup/plugin-commonjs": "^17.0.0", 15 | "@rollup/plugin-json": "^4.1.0", 16 | "@rollup/plugin-node-resolve": "^11.0.0", 17 | "@rollup/plugin-typescript": "^8.0.0", 18 | "@tsconfig/svelte": "^1.0.0", 19 | "@types/d3": "^6.7.0", 20 | "@types/d3-collection": "^1.0.10", 21 | "@types/d3-geo": "^2.0.0", 22 | "@types/mathjs": "^9.4.2", 23 | "@types/papaparse": "^5.2.5", 24 | "@types/topojson": "^3.2.3", 25 | "@types/topojson-client": "^3.1.0", 26 | "d3": "^6.7.0", 27 | "d3-collection": "^1.0.7", 28 | "d3-geo": "^2.0.1", 29 | "husky": "^6.0.0", 30 | "lint-staged": "^11.0.0", 31 | "mathjs": "^9.4.4", 32 | "papaparse": "^5.3.0", 33 | "postcss": "^8.2.13", 34 | "prettier": "2.3.0", 35 | "prettier-plugin-svelte": "^2.3.1", 36 | "rollup": "^2.3.4", 37 | "rollup-plugin-css-only": "^3.1.0", 38 | "rollup-plugin-livereload": "^2.0.0", 39 | "rollup-plugin-svelte": "^7.0.0", 40 | "rollup-plugin-terser": "^7.0.0", 41 | "sass": "^1.32.12", 42 | "simple-svelte-autocomplete": "^1.2.4", 43 | "svelte": "^3.0.0", 44 | "svelte-check": "^1.0.0", 45 | "svelte-preprocess": "^4.0.0", 46 | "topojson-client": "^3.1.0", 47 | "tslib": "^2.0.0", 48 | "typescript": "^4.0.0", 49 | "us-atlas": "^3.0.0" 50 | }, 51 | "dependencies": { 52 | "ansi-regex": "^6.0.1", 53 | "license-checker": "^25.0.1", 54 | "sirv-cli": "^1.0.0", 55 | "svelte-i18n": "^3.3.13" 56 | }, 57 | "lint-staged": { 58 | "*.{js,css,md}": "prettier --write" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement (CLA). You (or your employer) retain the copyright to your 10 | contribution; this simply gives us permission to use and redistribute your 11 | contributions as part of the project. Head over to 12 | to see your current agreements on file or 13 | to sign a new one. 14 | 15 | You generally only need to submit a CLA once, so if you've already submitted one 16 | (even if it was for a different project), you probably don't need to do it 17 | again. 18 | 19 | ## Code reviews 20 | 21 | All submissions, including submissions by project members, require review. We 22 | use GitHub pull requests for this purpose. Consult 23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 24 | information on using pull requests. 25 | 26 | ## Community Guidelines 27 | 28 | This project follows 29 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 30 | 31 | ## Contribution Workflow 32 | 33 | If you are a direct contributor, then please request access to collaborate on this repository, so you can work directly on the project using feature branches. 34 | 35 | After you have gained direct contributor access, you may follow this example workflow to get started: 36 | 37 | ```sh 38 | # grab the dev branch, after you create your local dev, this just becomes git checkout dev 39 | > git checkout -b dev origin/dev 40 | # make sure your local repo is up to date 41 | > git pull --all 42 | > git checkout -b feature-branch-name origin/dev 43 | # then make and stage your changes 44 | > git add . 45 | > git commit -m "Your first commit message" 46 | > git push -u origin feature-branch-name 47 | # You can now create a PR in github 48 | ``` 49 | Otherwise, please create and maintain a fork. -------------------------------------------------------------------------------- /src/global-vars.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | $grey-50: #f8f9fa; 18 | $grey-200: #e8eaed; 19 | $grey-300: #dadce0; 20 | $grey-400: #bdc1c6; 21 | $grey-500: #9aa0a6; 22 | $grey-600: #80868b; 23 | $grey-700: #5f6368; 24 | $grey-800: #3c4043; 25 | $grey-900: #202124; 26 | $blue-600: #1a73e8; 27 | $orange-600: #e8710a; 28 | $pink-400: #ff63b8; 29 | $pink-600: #e52592; 30 | $white: #ffffff; 31 | 32 | $body-font: "Roboto"; 33 | $body-weight: 400; 34 | $body-text: $grey-900; 35 | $body-background-color: $white; 36 | $header-light: $grey-700; 37 | $border-color: $grey-300; 38 | $highlight-color: $pink-400; 39 | $placeholder-color: $grey-700; 40 | $footer-background-color: $grey-50; 41 | $link-blue: $blue-600; 42 | $chip-background-selected: #e8f0ff; 43 | $chip-text-selected: #1967d2; 44 | $hover-card-box-shadow: #0000001f; 45 | $button-hover: #f1f3f4; 46 | 47 | $content-max-width: 800px; 48 | $header-height: 80px; 49 | $header-search-bar-height: 70px; 50 | $footer-height: 120px; 51 | $minimum-body-margin: 20px; 52 | $media-breakpoint: 600px; 53 | 54 | /** 55 | * Media Queries 56 | * 57 | * Example usage: 58 | * ``` 59 | * .my-div { 60 | * width: 100%; // Desktop 61 | * } 62 | * 63 | * @include mobileMaxMediaQuery { 64 | * width: 100px; // Mobile 65 | * } 66 | * ``` 67 | */ 68 | 69 | @mixin mobileMaxMediaQuery { 70 | @media only screen and (max-width: $media-breakpoint) { 71 | @content; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /public/data/VSI_metadata.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "countryCode": "AU", 4 | "dataFile": "AU_vaccination_search_insights.csv", 5 | "countryName": "Australia", 6 | "displayLevels": [0,1,2,3], 7 | "locales": ["en-AU"], 8 | "subRegion1Title": "States", 9 | "subRegion2Title": "Counties", 10 | "subRegion3Title": "Postcodes", 11 | "shapeFileLegal": "au", 12 | "placeId": "ChIJ38WHZwf9KysRUhNblaFnglM" 13 | }, 14 | { "countryCode": "CA", 15 | "dataFile":"CA_vaccination_search_insights.csv", 16 | "countryName":"Canada", 17 | "displayLevels":[0,1,2], 18 | "locales":["en-CA", "fr-CA"], 19 | "subRegion1Title": "Provinces", 20 | "subRegion2Title": "FSAs", 21 | "subRegion3Title": "", 22 | "shapeFileLegal": "ca", 23 | "placeId":"ChIJ2WrMN9MDDUsRpY9Doiq3aJk" 24 | }, 25 | { 26 | "countryCode": "IE", 27 | "dataFile": "IE_vaccination_search_insights.csv", 28 | "countryName": "Ireland", 29 | "displayLevels": [0,1], 30 | "locales": ["en-IE"], 31 | "subRegion1Title": "Counties", 32 | "subRegion2Title": "", 33 | "subRegion3Title": "", 34 | "shapeFileLegal": "ie", 35 | "placeId": "ChIJ-ydAXOS6WUgRCPTbzjQSfM8" 36 | }, 37 | { "countryCode":"GB", 38 | "dataFile":"GB_vaccination_search_insights.csv", 39 | "countryName":"United Kingdom", 40 | "displayLevels":[0,1,2,3], 41 | "locales":["en-GB"], 42 | "subRegion1Title": "UK countries", 43 | "subRegion2Title": "Regions", 44 | "subRegion3Title": "Postcodes", 45 | "shapeFileLegal":"gb", 46 | "placeId":"ChIJqZHHQhE7WgIReiWIMkOg-MQ" 47 | }, 48 | { "countryCode":"US", 49 | "dataFile": "US_initial.csv", 50 | "zipsFile": "US_zips.csv", 51 | "countryName":"United States", 52 | "displayLevels":[0,1,2,3], 53 | "locales":["en-US"], 54 | "subRegion1Title": "States", 55 | "subRegion2Title": "Counties", 56 | "subRegion3Title": "Zipcodes", 57 | "shapeFileLegal":"us", 58 | "placeId":"ChIJCzYy5IS16lQRQrfeQ5K5Oxw" 59 | } 60 | ] 61 | -------------------------------------------------------------------------------- /src/stores.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview The state of the application persisted to query parameters 19 | */ 20 | 21 | import { writable } from "svelte/store"; 22 | import type { RegionalTrends } from "./data"; 23 | import { fetchDateData } from "./data"; 24 | 25 | let rt = new Map(); 26 | let allDates: string[] 27 | 28 | type Params = { 29 | placeId: string; 30 | updateHistory: boolean; 31 | }; 32 | 33 | // TODO(patankar): Make browser back/forward update params after refactor. 34 | 35 | function loadParams(): Params { 36 | const searchParams = new URLSearchParams(window.location.search); 37 | const placeId = searchParams.has("placeId") 38 | ? searchParams.get("placeId") 39 | : ""; 40 | return { 41 | placeId, 42 | updateHistory: true, 43 | }; 44 | } 45 | 46 | function saveParams(param: Params) { 47 | if (param.placeId && param.updateHistory) { 48 | history.pushState(null, null, `?placeId=${param.placeId}`); 49 | } else if (!param.placeId && param.updateHistory) { 50 | history.pushState(null, null, "?") 51 | } 52 | } 53 | 54 | export const params = writable(loadParams()); 55 | params.subscribe(saveParams); 56 | window.onpopstate = function () { 57 | params.update((p) => { 58 | p = loadParams(); 59 | p.updateHistory = false; 60 | return p; 61 | }); 62 | }; 63 | 64 | // Using stores to keep the data to be used for the map visual. 65 | // This allows the data to be updated in cases where we load additional data later. 66 | // i.e. US zip codes. 67 | export const mapData = writable([]); 68 | // Using stores to keep the data to be used for the time series visuals. 69 | // This allows the data to be updated in cases where we load additional data later. 70 | // i.e. US zip codes. 71 | export const regionalTrends = writable(rt); 72 | 73 | // A parameter to track whether the zipcodes file has been downloaded. 74 | // This helps prevent repeatedly fetching the large file. 75 | export const isZipsDownloaded = writable(false); 76 | 77 | /** 78 | * A store to contain an array of all valid dates 79 | */ 80 | export const dateRange = writable(fetchDateData()); 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/zcta-county.ts: -------------------------------------------------------------------------------- 1 | // Since zip codes can span over multiple counties, this provides a poper taxonomy mapping 2 | // For the US, converted CSV at 3 | // http://www2.census.gov/geo/docs/maps-data/data/rel/zcta_county_rel_10.txt 4 | import usZctaCounty from "../public/geo/zcta-county-us.json"; 5 | import auZctaCounty from "../public/geo/zcta-county-au.json"; 6 | import gbCountyFipsCode from "../public/geo/gb-counties-fips.json"; 7 | import auCountyFipsCode from "../public/geo/au-counties-fips.json"; 8 | 9 | const AU_CAMBELLTOWN_CITY_COUNCIL_CODE = { 10 | "AU-NSW": "11500", 11 | "AU-SA": "40910", 12 | }; 13 | 14 | const usCountyZctaMap = usZctaCounty.reduce((acc,r)=> { 15 | acc.set(r.geoid,r.zcta) 16 | return acc; 17 | }, new Map>()); 18 | 19 | const auCountyZctaMap = auZctaCounty.reduce((acc, r) => { 20 | acc.set(r.geoid, r.zcta) 21 | return acc; 22 | }, new Map>()); 23 | 24 | /** 25 | * Mapping of county names as appear in data CSV file to county IDs as they appear in 26 | * the map shapefile for the UK 27 | */ 28 | const gbCountyFipsCodeMap = gbCountyFipsCode.reduce((acc, r) => { 29 | acc.set(r.county_name, r.county_fips_code) 30 | return acc; 31 | }, new Map()); 32 | 33 | /** 34 | * Mapping of county names as appear in data CSV file to county IDs as they appear in 35 | * the map shapefile for Australia 36 | */ 37 | const auCountyFipsCodeMap = auCountyFipsCode.reduce((acc, r) => { 38 | acc.set(r.county_name, r.county_fips_code) 39 | return acc; 40 | }, new Map()); 41 | 42 | /** 43 | * In case sub_region_2_code is missing in the data file (currently only relevant for AU and GB), 44 | * use the lookup table to match county name with the map shape ID of that county. 45 | */ 46 | export function getCountyFipsCode(county_name: string, state_name: string, country_code: string) { 47 | switch (country_code) { 48 | case "AU": 49 | // Region with the same county name, but located in different states 50 | if (county_name == "Campbelltown City Council") { 51 | return AU_CAMBELLTOWN_CITY_COUNCIL_CODE[state_name]; 52 | } else { 53 | return auCountyFipsCodeMap.get(county_name); 54 | } 55 | case "GB": 56 | return gbCountyFipsCodeMap.get(county_name); 57 | default: 58 | return "" 59 | } 60 | } 61 | 62 | /** 63 | * Get list of zip codes for the country 64 | */ 65 | export function getCountyZctas(fipsCode: string, country_code: string) { 66 | switch (country_code) { 67 | case "AU": 68 | return auCountyZctaMap.get(fipsCode); 69 | case "US": 70 | return usCountyZctaMap.get(fipsCode); 71 | default: 72 | return "" 73 | } 74 | } -------------------------------------------------------------------------------- /src/zip_data.ts: -------------------------------------------------------------------------------- 1 | import { parse, ParseResult } from "papaparse"; 2 | import { fetchRegionalTrendsData, RegionalTrendLine, RegionalTrends } from "./data"; 3 | import { mapData, regionalTrends } from "./stores"; 4 | 5 | const US_ZIP_FILENAMES = ["./data/US_zips_2022.csv", "./data/US_zips_2023.csv"]; 6 | 7 | function coerceNumber(u: unknown) { 8 | if (u === "") { 9 | return NaN; 10 | } else { 11 | return Number.parseFloat(u as string); 12 | } 13 | } 14 | 15 | export function fetchZipTrendLines(filename: string): Promise { 16 | let results: Promise = new Promise( 17 | (resolve, reject) => { 18 | parse(filename /*change this if the file location changes*/, { 19 | download: true, 20 | header: true, 21 | skipEmptyLines: true, 22 | complete: function (results: ParseResult) { 23 | console.log( 24 | `Load regional zips trend data with ${results.data.length} from zips file ` 25 | ); 26 | const mappedData = results.data.map((d) => { 27 | const parsedRow: RegionalTrendLine = { 28 | date: d.date, 29 | country_region: d.country_region, 30 | country_region_code: d.country_region_code, 31 | sub_region_1: d.sub_region_1, 32 | sub_region_1_code: d.sub_region_1_code, 33 | sub_region_2: d.sub_region_2, 34 | sub_region_2_code: d.sub_region_2_code, 35 | sub_region_3: d.sub_region_3, 36 | sub_region_3_code: d.sub_region_3_code, 37 | place_id: d.place_id, 38 | //We need to coerce number parsing since papaparse only gives us strings 39 | sni_covid19_vaccination: coerceNumber( 40 | d.sni_covid19_vaccination 41 | ), 42 | sni_vaccination_intent: coerceNumber(d.sni_vaccination_intent), 43 | sni_safety_side_effects: coerceNumber( 44 | d.sni_safety_side_effects 45 | ), 46 | }; 47 | return parsedRow; 48 | }); 49 | 50 | resolve(mappedData); 51 | }, 52 | }); 53 | } 54 | ); 55 | return results; 56 | } 57 | 58 | 59 | export async function updateWithZipsData() { 60 | let zipsTrendLineData: RegionalTrendLine[] = []; 61 | for (const filename of US_ZIP_FILENAMES) { 62 | zipsTrendLineData = zipsTrendLineData.concat(await fetchZipTrendLines(filename)) 63 | } 64 | 65 | mapData.update((m) => m.concat(zipsTrendLineData)); 66 | 67 | let regT: Map; 68 | regionalTrends.subscribe((map) => { regT = map }); 69 | let RegionalTrendZipData = fetchRegionalTrendsData(Promise.all(zipsTrendLineData)) 70 | RegionalTrendZipData.then((rTZD) => { 71 | for (let [k, v] of rTZD) { 72 | regT.set(k, v); 73 | } 74 | regionalTrends.set(regT); 75 | }); 76 | 77 | return [zipsTrendLineData, RegionalTrendZipData] 78 | } 79 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import svelte from 'rollup-plugin-svelte'; 18 | import commonjs from '@rollup/plugin-commonjs'; 19 | import resolve from '@rollup/plugin-node-resolve'; 20 | import livereload from 'rollup-plugin-livereload'; 21 | import { terser } from 'rollup-plugin-terser'; 22 | import sveltePreprocess from 'svelte-preprocess'; 23 | import typescript from '@rollup/plugin-typescript'; 24 | import css from 'rollup-plugin-css-only'; 25 | import json from '@rollup/plugin-json'; 26 | 27 | const production = !process.env.ROLLUP_WATCH; 28 | 29 | function serve() { 30 | let server; 31 | 32 | function toExit() { 33 | if (server) server.kill(0); 34 | } 35 | 36 | return { 37 | writeBundle() { 38 | if (server) return; 39 | server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { 40 | stdio: ['ignore', 'inherit', 'inherit'], 41 | shell: true 42 | }); 43 | 44 | process.on('SIGTERM', toExit); 45 | process.on('exit', toExit); 46 | } 47 | }; 48 | } 49 | 50 | export default { 51 | input: 'src/main.ts', 52 | output: { 53 | sourcemap: true, 54 | format: 'iife', 55 | name: 'app', 56 | file: 'public/build/bundle.js' 57 | }, 58 | plugins: [ 59 | svelte({ 60 | preprocess: sveltePreprocess({ sourceMap: !production }), 61 | compilerOptions: { 62 | // enable run-time checks when not in production 63 | dev: !production, 64 | } 65 | }), 66 | // we'll extract any component CSS out into 67 | // a separate file - better for performance 68 | css({ output: 'bundle.css' }), 69 | 70 | // If you have external dependencies installed from 71 | // npm, you'll most likely need these plugins. In 72 | // some cases you'll need additional configuration - 73 | // consult the documentation for details: 74 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 75 | resolve({ 76 | browser: true, 77 | dedupe: ['svelte'] 78 | }), 79 | commonjs(), 80 | typescript({ 81 | sourceMap: !production, 82 | inlineSources: !production 83 | }), 84 | json(), 85 | // In dev mode, call `npm run start` once 86 | // the bundle has been generated 87 | !production && serve(), 88 | 89 | // Watch the `public` directory and refresh the 90 | // browser on changes when not in production 91 | !production && livereload('public'), 92 | 93 | // If we're building for production (npm run build 94 | // instead of npm run dev), minify 95 | production && terser() 96 | ], 97 | watch: { 98 | clearScreen: false 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | SRC_DIR = public/data 2 | 3 | $(SRC_DIR): 4 | mkdir -p $@ 5 | 6 | US_vaccination_search_insights.csv: $(SRC_DIR) 7 | curl -o $(SRC_DIR)/US_vaccination_search_insights.csv https://storage.googleapis.com/covid19-open-data/covid19-vaccination-search-insights/US_vaccination_search_insights.csv 8 | 9 | GB_vaccination_search_insights.csv: $(SRC_DIR) 10 | curl -o $(SRC_DIR)/GB_vaccination_search_insights.csv https://storage.googleapis.com/covid19-open-data/covid19-vaccination-search-insights/GB_vaccination_search_insights.csv 11 | 12 | # modifying the contents of CA_vaccination_search_insights.csv to move the FSAs up to county level so simplify data visualization processing and leaving the original data for download unchanged. 13 | CA_vaccination_search_insights.csv: $(SRC_DIR) 14 | curl -o $(SRC_DIR)/CA_temp.csv https://storage.googleapis.com/covid19-open-data/covid19-vaccination-search-insights/CA_vaccination_search_insights.csv 15 | cat $(SRC_DIR)/CA_temp.csv | awk 'NR==1' > $(SRC_DIR)/$@ 16 | cat $(SRC_DIR)/CA_temp.csv | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'NR>1 { print $$1,$$2,$$3,$$4,$$5,$$9,$$9,"","",$$10,$$11,$$12,$$13 }' >> $(SRC_DIR)/$@ 17 | rm -f $(SRC_DIR)/CA_temp.csv 18 | 19 | IE_vaccination_search_insights.csv: $(SRC_DIR) 20 | curl -o $(SRC_DIR)/IE_vaccination_search_insights.csv https://storage.googleapis.com/covid19-open-data/covid19-vaccination-search-insights/IE_vaccination_search_insights.csv 21 | 22 | AU_vaccination_search_insights.csv: $(SRC_DIR) 23 | curl -o $(SRC_DIR)/AU_vaccination_search_insights.csv https://storage.googleapis.com/covid19-open-data/covid19-vaccination-search-insights/AU_vaccination_search_insights.csv 24 | 25 | Global_l0_vaccination_search_insights.csv: US_vaccination_search_insights.csv GB_vaccination_search_insights.csv CA_vaccination_search_insights.csv IE_vaccination_search_insights.csv AU_vaccination_search_insights.csv 26 | cat $(SRC_DIR)/$(word 1,$^) | awk 'NR==1' > $(SRC_DIR)/$@ 27 | cat $(SRC_DIR)/$(word 1,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'length($$4) < 1' >> $(SRC_DIR)/$@ 28 | cat $(SRC_DIR)/$(word 2,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'length($$4) < 1' >> $(SRC_DIR)/$@ 29 | cat $(SRC_DIR)/$(word 3,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'length($$4) < 1' >> $(SRC_DIR)/$@ 30 | cat $(SRC_DIR)/$(word 4,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'length($$4) < 1' >> $(SRC_DIR)/$@ 31 | cat $(SRC_DIR)/$(word 5,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'length($$4) < 1' >> $(SRC_DIR)/$@ 32 | 33 | gb_regions.csv: $(SRC_DIR)/gb_regions.csv 34 | au_regions.csv: $(SRC_DIR)/au_regions.csv 35 | 36 | regions.csv: US_vaccination_search_insights.csv gb_regions.csv au_regions.csv CA_vaccination_search_insights.csv IE_vaccination_search_insights.csv 37 | cat $(SRC_DIR)/$(word 1,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' '{ print $$2,$$3,$$4,$$5,$$6,$$7,$$8,$$9,$$10 }' | uniq > $(SRC_DIR)/$@ 38 | cat $(SRC_DIR)/$(word 2,$^) | awk 'NR>1' >> $(SRC_DIR)/$@ 39 | cat $(SRC_DIR)/$(word 3,$^) | awk 'NR>1' >> $(SRC_DIR)/$@ 40 | cat $(SRC_DIR)/$(word 4,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'NR>1 { print $$2,$$3,$$4,$$5,$$6,$$7,$$8,$$9,$$10 }' | uniq >> $(SRC_DIR)/$@ 41 | cat $(SRC_DIR)/$(word 5,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'NR>1 { print $$2,$$3,$$4,$$5,$$6,$$7,$$8,$$9,$$10 }' | uniq >> $(SRC_DIR)/$@ 42 | 43 | US_initial.csv: US_vaccination_search_insights.csv 44 | cat $(SRC_DIR)/$(word 1,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' '($$8) != "postal_code"' > $(SRC_DIR)/$@ 45 | 46 | US_zips_2021.csv: US_vaccination_search_insights.csv 47 | cat $(SRC_DIR)/$(word 1,$^) | awk 'NR==1' > $(SRC_DIR)/$@ 48 | cat $(SRC_DIR)/$(word 1,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' '($$8) == "postal_code" && index($$1, "2021")' >> $(SRC_DIR)/$@ 49 | 50 | US_zips_2022.csv: US_vaccination_search_insights.csv 51 | cat $(SRC_DIR)/$(word 1,$^) | awk 'NR==1' > $(SRC_DIR)/$@ 52 | cat $(SRC_DIR)/$(word 1,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' '($$8) == "postal_code" && index($$1, "2022")' >> $(SRC_DIR)/$@ 53 | 54 | US_zips_2023.csv: US_vaccination_search_insights.csv 55 | cat $(SRC_DIR)/$(word 1,$^) | awk 'NR==1' > $(SRC_DIR)/$@ 56 | cat $(SRC_DIR)/$(word 1,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' '($$8) == "postal_code" && index($$1, "2023")' >> $(SRC_DIR)/$@ 57 | rm -f $(SRC_DIR)/$(word 1,$^) 58 | 59 | All_dates.csv: Global_l0_vaccination_search_insights.csv 60 | cat $(SRC_DIR)/$(word 1,$^) | awk -vFPAT='[^,]*|"[^"]*"' -v OFS=',' 'NR>1 { print $$1 }' | sort -u | uniq >> $(SRC_DIR)/$@ 61 | 62 | data: Global_l0_vaccination_search_insights.csv AU_vaccination_search_insights.csv GB_vaccination_search_insights.csv regions.csv IE_vaccination_search_insights.csv CA_vaccination_search_insights.csv US_initial.csv US_zips_2021.csv US_zips_2022.csv US_zips_2023.csv All_dates.csv 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vaccination Search Insights 2 | 3 | This project provides an reference visualization for the Google [Vaccination Search Insights](http://console.cloud.google.com/marketplace/product/bigquery-public-datasets/covid19-vaccination-search-insights) data set. 4 | 5 | This is not an official Google product. 6 | 7 | Keep reading or [download the data](https://storage.googleapis.com/covid19-open-data/covid19-vaccination-search-insights/Global_vaccination_search_insights.csv) directly. 8 | 9 | ## About this data 10 | 11 | You can use this data to compare search interest between topics related to COVID-19 vaccination. The value for search interest isn’t an absolute number of searches—it’s a value representing relative interest which we scale to make it easier to compare regions with one another, or the same region over time. If you’d like to know more about our calculation and process, see our [technical docs](https://storage.googleapis.com/gcs-public-datasets/COVID-19%20Vaccination%20Search%20Insights%20documentation.pdf). 12 | ## How to best use this data 13 | 14 | We used the same normalization and scaling everywhere so that you can make these comparisons: 15 | 16 | * Compare a region with others to see where you might focus effort. 17 | * Compare a region over time to see how your community’s information needs have changed or see the impact of your communication efforts and news events. 18 | 19 | Remember, the data shows people’s interest—not opinions or actual events. You can’t conclude that a community is suffering from many side effects because there’s increased interest in the safety and side effects category. 20 | 21 | Protecting privacy 22 | We developed the Vaccine Search Insights to be helpful while adhering to our stringent privacy protocols for search data. No individual search queries or other personally identifiable information are made available at any point. For this data, we use [differential privacy](https://www.youtube.com/watch?v=FfAdemDkLsc), which adds artificial noise to our data while enabling high quality results without identifying any individual person. 23 | ## Availability and updates 24 | 25 | To download or use the data or insights, you must agree to the [Google Terms of Service](https://policies.google.com/terms). 26 | 27 | We’ll update the data each week. You can check the dates in the charts to see the most recent day in the data. If you download the CSV, remember to get an updated version each week. 28 | 29 | We'll continue to update this product while public health experts find it useful in their COVID-19 vaccination efforts. Our published data will remain publicly available to support long-term research and evaluation. 30 | 31 | ### Query the dataset 32 | 33 | Get real-time insights using Google Cloud’s BigQuery. Analyse with SQL or call APIs from your code. 34 | 35 | [Bigquery public dataset](http://console.cloud.google.com/marketplace/product/bigquery-public-datasets/covid19-vaccination-search-insights) 36 | 37 | ### Analyze with covariates 38 | 39 | Analyze the data alongside other covariates in the [COVID-19 Open Data Repository](https://github.com/GoogleCloudPlatform/covid-19-open-data). 40 | 41 | ### Tell us about your project 42 | 43 | We’d love to hear more about how you’re using Vaccination Search Insights. If you’ve solved problems, we’d like to help you share your solutions. 44 | 45 | [covid-19-search-trends-feedback@google.com](mailto:covid-19-search-trends-feedback@google.com) 46 | 47 | ## Dashboard development 48 | 49 | ### Getting started 50 | 51 | This project should build in most posix-like enviroments. Besides `npm`, it has small dependencies on `make` and `wget`. 52 | 53 | Install the dependencies... 54 | 55 | ```bash 56 | npm install 57 | ``` 58 | 59 | ...then start [Rollup](https://rollupjs.org): 60 | 61 | ```bash 62 | npm run dev 63 | ``` 64 | 65 | Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes. 66 | 67 | ### Development process and releasing 68 | 69 | All changes require a PR. Normal feature PRs are merged into `dev`. When it's time for a release: 70 | 71 | Install https://github.com/release-it/release-it 72 | 73 | ```bash 74 | $ npm install -g release-it 75 | ``` 76 | Then create the release branch from dev and push it as a new PR. Say you want to release version 1.2.3 77 | 78 | ```bash 79 | $ git checkout dev 80 | $ git pull --all 81 | $ git checkout -b release-v1.2.3 origin/dev 82 | $ git push -u origin release-v1.2.3 83 | $ release-it 1.2.3 84 | ``` 85 | 86 | Then create the PR and make sure that the PR will merge into main. If there are any issues found during release testing, they can be made directly on the release branch or cherry-picked from `dev`. 87 | 88 | Afterwards there should be a sync PR from main back into dev (like #93). 89 | 90 | ### Deployment 91 | 92 | `.github/gh-pages.yml` defines a process publishes build output to a special branch called `gh-pages`, whenever there is a new push (e.g. PR merging) on the `main` branch. This workflow can also be manually triggered, which may be handy to include a new version of the data set. 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Region, RegionType } from "./data"; 18 | import * as d3 from "d3"; 19 | 20 | let activePopupId: string; 21 | 22 | export function getRegionName(region: Region): string { 23 | let regionName: string; 24 | let parentRegionName: string; 25 | 26 | if (!region) { 27 | return ""; 28 | } 29 | 30 | if (region.region_type === RegionType.SubRegionThree) { 31 | regionName = region[`${region.region_type}_code`]; 32 | } else { 33 | regionName = region[region.region_type]; 34 | } 35 | 36 | parentRegionName = region[region.parent_region_type]; 37 | 38 | if (parentRegionName) { 39 | return `${regionName}, ${parentRegionName}`; 40 | } else { 41 | return regionName; 42 | } 43 | } 44 | 45 | export function getCountryName(region: Region): string{ 46 | return region.country_region; 47 | } 48 | 49 | export function getCountryCode(region: Region): string { 50 | return region.country_region_code; 51 | } 52 | 53 | export function inClientBounds( 54 | clientX: number, 55 | clientY: number, 56 | bounds: DOMRect 57 | ): boolean { 58 | return ( 59 | clientX >= bounds.left && 60 | clientX <= bounds.right && 61 | clientY >= bounds.top && 62 | clientY <= bounds.bottom 63 | ); 64 | } 65 | 66 | export function handleInfoPopup(event, id): void { 67 | if (id === activePopupId) { 68 | dismissInfoPopup(event); 69 | } else { 70 | const popup: d3.Selection = d3.select(id); 71 | const infoRect: DOMRect = event.target.getBoundingClientRect(); 72 | const popupRectWidth = 310; 73 | const rightPadding = 20; 74 | 75 | if (activePopupId) { 76 | dismissInfoPopup(event); 77 | } 78 | 79 | const offsetOnRight = infoRect.x + infoRect.width + window.pageXOffset; 80 | 81 | const left = 82 | offsetOnRight + popupRectWidth > window.innerWidth 83 | ? window.innerWidth - popupRectWidth - rightPadding 84 | : offsetOnRight; 85 | 86 | popup 87 | .style("display", "block") 88 | .style("left", `${left}px`) 89 | .style("top", `${infoRect.y + infoRect.height + window.pageYOffset}px`); 90 | 91 | event.stopPropagation(); 92 | activePopupId = id; 93 | document.addEventListener("click", dismissInfoPopup); 94 | } 95 | } 96 | 97 | function dismissInfoPopup(event): void { 98 | const popup: d3.Selection = 99 | d3.select(activePopupId); 100 | if ( 101 | !inClientBounds( 102 | event.clientX, 103 | event.clientY, 104 | popup.node().getBoundingClientRect() 105 | ) 106 | ) { 107 | popup.style("display", "none"); 108 | document.removeEventListener("click", dismissInfoPopup); 109 | activePopupId = null; 110 | event.stopPropagation(); 111 | } 112 | } 113 | 114 | type HtmlSelection = d3.Selection; 115 | type SvgSelection = d3.Selection; 116 | type ElementSection = HtmlSelection | SvgSelection; 117 | 118 | export function formatDateForDisplay(date: Date): string { 119 | const options: Intl.DateTimeFormatOptions = { 120 | month: "short", 121 | day: "numeric", 122 | year: "numeric", 123 | }; 124 | return new Intl.DateTimeFormat("en-US", options).format(date); 125 | } 126 | 127 | export function formatDateForStorage(date: Date): string { 128 | const month = new Intl.DateTimeFormat("en-US", { month: "2-digit" }).format( 129 | date 130 | ); 131 | const day = new Intl.DateTimeFormat("en-US", { day: "2-digit" }).format( 132 | date 133 | ); 134 | const year = new Intl.DateTimeFormat("en-US", { year: "numeric" }).format( 135 | date 136 | ); 137 | 138 | return `${year}-${month}-${day}`; 139 | } 140 | 141 | export function convertStorageDate(storageDate: string): Date { 142 | const date = new Date(storageDate); 143 | const time = date.getTime(); 144 | const timeZoneOffset = date.getTimezoneOffset() * 60 * 1000; 145 | const adjustedTime = time + timeZoneOffset; 146 | 147 | return new Date(adjustedTime); 148 | } 149 | 150 | export function getClosestDate(selected, earlier, later) { 151 | if (!earlier && later) { 152 | return later; 153 | } else if (!later && earlier) { 154 | return earlier; 155 | } else if ( 156 | selected.getTime() - earlier.getTime() < 157 | later.getTime() - selected.getTime() 158 | ) { 159 | return earlier; 160 | } else { 161 | return later; 162 | } 163 | } -------------------------------------------------------------------------------- /src/geo-utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import type { UsAtlas } from "topojson"; 18 | import * as us from "us-atlas/counties-albers-10m.json"; 19 | import type { Region } from "./data"; 20 | 21 | export const dcStateFipsCode: string = "11"; 22 | export const dcCountyFipsCode: string = "11001"; 23 | 24 | // Used to make sure we don't fetch GB postal code centroids once it was already loaded 25 | let gb_postal_centroids; 26 | 27 | export const regionOneToFipsCode: Map> = new Map([ 28 | ['US', new Map([ 29 | ["US-AL", "01"], 30 | ["US-AK", "02"], 31 | ["US-AZ", "04"], 32 | ["US-AR", "05"], 33 | ["US-CA", "06"], 34 | ["US-CO", "08"], 35 | ["US-CT", "09"], 36 | ["US-DE", "10"], 37 | ["US-DC", "11"], 38 | ["US-FL", "12"], 39 | ["US-GA", "13"], 40 | ["US-HI", "15"], 41 | ["US-ID", "16"], 42 | ["US-IL", "17"], 43 | ["US-IN", "18"], 44 | ["US-IA", "19"], 45 | ["US-KS", "20"], 46 | ["US-KY", "21"], 47 | ["US-LA", "22"], 48 | ["US-ME", "23"], 49 | ["US-MD", "24"], 50 | ["US-MA", "25"], 51 | ["US-MI", "26"], 52 | ["US-MN", "27"], 53 | ["US-MS", "28"], 54 | ["US-MO", "29"], 55 | ["US-MT", "30"], 56 | ["US-NE", "31"], 57 | ["US-NV", "32"], 58 | ["US-NH", "33"], 59 | ["US-NJ", "34"], 60 | ["US-NM", "35"], 61 | ["US-NY", "36"], 62 | ["US-NC", "37"], 63 | ["US-ND", "38"], 64 | ["US-OH", "39"], 65 | ["US-OK", "40"], 66 | ["US-OR", "41"], 67 | ["US-PA", "42"], 68 | ["US-RI", "44"], 69 | ["US-SC", "45"], 70 | ["US-SD", "46"], 71 | ["US-TN", "47"], 72 | ["US-TX", "48"], 73 | ["US-UT", "49"], 74 | ["US-VT", "50"], 75 | ["US-VA", "51"], 76 | ["US-WA", "53"], 77 | ["US-WV", "54"], 78 | ["US-WI", "55"], 79 | ["US-WY", "56"], 80 | ["US-AS", "60"], 81 | ["US-GU", "66"], 82 | ["US-MP", "69"], 83 | ["US-PR", "72"], 84 | ["US-VI", "78"]])], 85 | ['GB', new Map([ 86 | ["GB-ENG", "E"], 87 | ["GB-SCT", "S"], 88 | ["GB-WLS", "W"], 89 | ["GB-NIR", "N"]])], 90 | ['CA', new Map([ 91 | ["CA-BC", "59"], 92 | ["CA-AB", "48"], 93 | ["CA-SK", "47"], 94 | ["CA-MB", "46"], 95 | ["CA-ON", "35"], 96 | ["CA-QC", "24"], 97 | ["CA-NB", "13"], 98 | ["CA-NS", "12"], 99 | ["CA-PE", "11"], 100 | ["CA-NL", "10"], 101 | ["CA-YT", "60"], 102 | ["CA-NT", "61"], 103 | ["CA-NU", "62"]])], 104 | ['IE', new Map([ 105 | ["IE-DL", "1"], 106 | ["IE-LK", "2"], 107 | ["IE-KE", "3"], 108 | ["IE-WD", "4"], 109 | ["IE-D", "5"], 110 | ["IE-WH", "6"], 111 | ["IE-MN", "7"], 112 | ["IE-WW", "8"], 113 | ["IE-CO", "9"], 114 | ["IE-KY", "10"], 115 | ["IE-RN", "11"], 116 | ["IE-WX", "12"], 117 | ["IE-LD", "13"], 118 | ["IE-MH", "14"], 119 | ["IE-CN", "15"], 120 | ["IE-CW", "16"], 121 | ["IE-MO", "17"], 122 | ["IE-LH", "18"], 123 | ["IE-SO", "19"], 124 | ["IE-LM", "20"], 125 | ["IE-KK", "21"], 126 | ["IE-OY", "22"], 127 | ["IE-LS", "23"], 128 | ["IE-G", "24"], 129 | ["IE-TA", "25"], 130 | ["IE-CE", "26"]])], 131 | ['AU', new Map([ 132 | ["AU-NSW", "1"], 133 | ["AU-VIC", "2"], 134 | ["AU-QLD", "3"], 135 | ["AU-SA", "4"], 136 | ["AU-WA", "5"], 137 | ["AU-TAS", "6"], 138 | ["AU-NT", "7"], 139 | ["AU-ACT", "8"], 140 | ["AU-OT", "9"] 141 | ])] 142 | ]); 143 | 144 | const CA_STATE_IDS: Map = new Map([ 145 | ["V", "59"], 146 | ["T", "48"], 147 | ["S", "47"], 148 | ["R", "46"], 149 | 150 | ["K", "35"], 151 | ["L", "35"], 152 | ["M", "35"], 153 | ["N", "35"], 154 | ["P", "35"], 155 | 156 | ["G", "24"], 157 | ["H", "24"], 158 | ["J", "24"], 159 | 160 | ["E", "13"], 161 | ["B", "12"], 162 | ["C", "11"], 163 | ["A", "10"] 164 | ]) 165 | 166 | export function stateFipsCodeFromCounty(countyFipsCode: string, countryCode): string { 167 | if (countryCode == "US") { 168 | return countyFipsCode.slice(0, 2); 169 | } else if (countryCode == "GB") { 170 | return countyFipsCode.slice(0, 1); 171 | } else if (countryCode == "AU") { 172 | return countyFipsCode.slice(0, 1); 173 | } else if (countryCode == "CA") { 174 | return CA_STATE_IDS.get(countyFipsCode.slice(0, 1)); 175 | } 176 | } 177 | 178 | export function fipsCodeFromElementId(id: string): string { 179 | return id.split("-")[1]; 180 | } 181 | 182 | // Get the name of the level from element ID (if "postcode-E14", returns "postcode") 183 | export function levelNameFromElementId(id: string): string { 184 | return id.split("-")[0]; 185 | } 186 | 187 | export function getUSAtlas(): UsAtlas { 188 | return us as unknown as UsAtlas; 189 | } 190 | 191 | export function getAtlas(countryCode: string): Promise { 192 | return fetch("./geo/" + countryCode.toLocaleLowerCase() + "-albers.json" 193 | ).then((response) => 194 | response.json() 195 | ); 196 | } 197 | 198 | export function getGbPostalCentroids() { 199 | if (gb_postal_centroids) { 200 | return gb_postal_centroids; 201 | } else { 202 | gb_postal_centroids = fetch("./geo/gb-postal-albers.json").then((response) => 203 | response.json() 204 | ); 205 | return gb_postal_centroids; 206 | } 207 | } 208 | 209 | export function buildRegionCodeToPlaceIdMapping( 210 | regions: Region[], countryCode 211 | ): Map { 212 | return regions.reduce((acc, region) => { 213 | if (region.sub_region_3_code != "") { 214 | // TODO: Ignore zipcodes-level data for now. 215 | // Can't just use sub_region_3_code directly because it overlaps with 216 | // sub_region_2_code, eg '06043' is a zipcode in CT and fips in CA. 217 | } else if (region.sub_region_2_code == "") { 218 | acc.set( 219 | regionOneToFipsCode.get(countryCode).get(region.sub_region_1_code), 220 | region.place_id 221 | ); 222 | } else { 223 | acc.set(region.sub_region_2_code, region.place_id); 224 | } 225 | return acc; 226 | }, new Map()); 227 | } 228 | -------------------------------------------------------------------------------- /public/lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_title": "COVID-19 Vaccination Search Insights", 3 | "content": { 4 | "COVID_19_VACCINATION_TITLE": "COVID-19 vaccination searches", 5 | "SAFETY_SIDE_EFFECTS_TITLE": "Safety and side effect searches", 6 | "VACCINATION_INTENT_TITLE": "Vaccination intent searches", 7 | "select_country": "Select a country to see more insights", 8 | "countries": "Countries", 9 | "region_name": "Region Name", 10 | "app_overview": "
The COVID-19 Vaccination Search Insights are no longer being updated as of 2024-02-05. All historical data will remain publicly available.

Explore searches for COVID-19 vaccination topics by region. This aggregated and anonymized data helps you understand and compare communities' information needs. We’re releasing this data to inform public health vaccine-confidence efforts. Learn more", 11 | "about_data_title": "About this data", 12 | "about_data_content": "You can use this data to compare search interest between topics related to COVID-19 vaccination and see what topics are popular or rising. The value for search interest isn't an absolute number of searches—it's a value representing relative interest which we scale to make it easier to compare regions with one another, or the same region over time. If you'd like to know more about our calculation and process, see our technical docs.", 13 | "best_use_title": "How to best use this data", 14 | "best_use_content": "

We used the same normalization and scaling everywhere so that you can make these comparisons:

  • Compare a region with others to see where you might focus effort.
  • Compare a region over time to see how your community's information needs have changed or see the impact of your communication efforts and news events.

Remember, the data shows people's interest—not opinions or actual events. You can't conclude that a community is suffering from many side effects because there's increased interest in the safety and side effects category.

", 15 | "protecting_privacy_title": "Protecting privacy", 16 | "proctecting_privacy_content": "

We developed the Vaccine Search Insights to be helpful while adhering to our stringent privacy protocols for search data. No individual search queries or other personally identifiable information are made available at any point. For this data, we use differential privacy, which adds artificial noise to our data while enabling high quality results without identifying any individual person. Additionally, we don't show data for regions that are smaller than 3 km2.

To learn more about the privacy methods used to generate the data, read the anonymization process description.

", 17 | "case_studies_title": "Case studies", 18 | "availability_title": "Availability and updates", 19 | "availability_content": "

To download or use the data or insights, you must agree to the Google Terms of Service.

We'll update the data each week. You can check the dates in the charts to see the most recent day in the data. If you download the CSV, remember to get an updated version each week.

We'll continue to update this product while public health experts find it useful in their COVID-19 vaccination efforts. Our published data will remain publicly available to support long-term research and evaluation.

", 20 | "next_steps": { 21 | "query_dataset_title": "Query the dataset with SQL", 22 | "query_dataset_content": "Get insights using Google Cloud’s BigQuery. Analyze with SQL, generate reports, or call the API from your code.", 23 | "query_dataset_link": "Bigquery public dataset", 24 | "analyze_covariate_title": "Analyze with covariate data", 25 | "analyze_covariate_content": "Analyze this data alongside other covariates in the COVID-19 Open-Data repository.", 26 | "analyze_covariate_link": "Github repository", 27 | "feedback_title": "Tell us about your project", 28 | "feedback_content": "We'd love to hear more about how you're using the data. Send feedback using our form.", 29 | "feedback_link": "Feedback" 30 | }, 31 | "download_popup": { 32 | "terms": "In order to download or use the data or insights, you must agree to the Google Terms of Service.", 33 | "download_dataset": "DOWNLOAD DATASETS", 34 | "countries": { 35 | "australia": "Australia", 36 | "canada": "Canada", 37 | "uk": "United Kingdom", 38 | "usa": "United States", 39 | "ireland": "Ireland" 40 | } 41 | }, 42 | "top_queries": { 43 | "top_searches": "Top Searches", 44 | "info": "info", 45 | "done": "done", 46 | "rising": "Rising", 47 | "related": "Related Queries" 48 | }, 49 | "case_studies": { 50 | "title": "Case studies", 51 | "pgp_title": "The Public Good Projects", 52 | "grapevine_title": "Grapevine Health", 53 | "pgp_desc": "Using Google's Vaccine Search Insights to increase vaccine confidence among BIPOC communities", 54 | "grapevine_desc": "Augmenting intelligence about high yield locations for vaccination outreach" 55 | } 56 | }, 57 | "navigation": { 58 | "download_data": "Download Data", 59 | "documentation": "Documentation", 60 | "country_picker": "Select a country", 61 | "map_interest": "Interest", 62 | "footer": { 63 | "about": "About Google", 64 | "products": "Google Products", 65 | "privacy": "Privacy", 66 | "terms": "Terms", 67 | "third_party_notices": "3rd Parties" 68 | } 69 | }, 70 | "hints": { 71 | "country_selector": "Select a country to see more insights", 72 | "loading_data": "Loading data...", 73 | "click_to_drill": "Click to drill down" 74 | }, 75 | "tooltips": { 76 | "learn_more": "Learn more", 77 | "parent_tooltip": "This parent category includes searches from the other two subcategories.", 78 | "vaccine_preamble": "Search interest in any aspect of COVID-19 vaccination. For example, “when can i get the covid vaccine” or ", 79 | "intent_preamble": "Search interest in the eligibility, availability, and accessibility of COVID-19 vaccines. For example, “covid vaccine near me” or ", 80 | "vaccine_country_example": { 81 | "CA": "vaccine tracker", 82 | "IE": "HSE covid vaccine", 83 | "GB": "NHS covid vaccine", 84 | "US": "cdc vaccine tracker", 85 | "AU": "covid vaccine" 86 | }, 87 | "intent_country_example": { 88 | "CA": "covid vaccine ontario", 89 | "IE": "book HSE covid vaccine", 90 | "GB": "book NHS covid vaccine", 91 | "US": "safeway covid vaccine", 92 | "AU": "pfizer vaccine melbourne" 93 | }, 94 | "scaled_tooltip": "A scaled value that you can compare across regions, times, or topics.", 95 | "safety_tooltip": "Search interest in the safety and side effects of COVID-19 vaccines. For example, \"is the covid vaccine safe\" or \"pfizer vaccine side effects\". A scaled value that you can compare across regions, times, or topics.", 96 | "vaccine_top_query": "Most common searches related to any aspect of COVID-19 vaccination, listed in order of frequency.", 97 | "safety_top_query": "Most common searches related to the safety and side effects of COVID-19 vaccines, listed in order of frequency.", 98 | "intent_top_query": "Most common searches related to the eligibility, availability, and accessibility of COVID-19 vaccines, listed in order of frequency." 99 | }, 100 | "legend": { 101 | "no_data": "Not enough data", 102 | "interest": "Interest", 103 | "hi": "High", 104 | "lo": "Low", 105 | "change": "Change" 106 | }, 107 | "quote": { 108 | "start": "\"", 109 | "end": "\"" 110 | }, 111 | "map_license": { 112 | "us": "Chart includes data from the US Census Bureau", 113 | "gb": "Chart includes data from the Office for National Statistics licensed under the Open Government Licence v.3.0", 114 | "ie": "Chart includes data from Ordnance Survey Ireland licensed under CC:BY 4.0", 115 | "ca": "Chart includes data adapted from Statistics Canada, Boundary Files 2016. This does not constitute an endorsement by Statistics Canada of this product.", 116 | "au": "Chart includes Administrative Boundaries from the Australian Bureau of Statistics licensed under Creative Commons Attribution 4.0 International license (CC BY 4.0) accessed March 31, 2022." 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /public/lang/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_title": "Statistiques de recherche sur le vaccination contre la COVID-19", 3 | "content": { 4 | "COVID_19_VACCINATION_TITLE": "Recherches sur la vaccination contre la COVID-19", 5 | "SAFETY_SIDE_EFFECTS_TITLE": "Recherches sur l'innocuité et les effets secondaires", 6 | "VACCINATION_INTENT_TITLE": "Recherches sur les intentions de vaccination", 7 | "select_country": "Sélectionnez un pays pour voir plus de statistiques", 8 | "countries": "Pays", 9 | "region_name": "Nom de la région", 10 | "app_overview": "
The COVID-19 Vaccination Search Insights are no longer being updated as of 2024-02-05. All historical data will remain publicly available.

Explorez les recherches de sujets sur la vaccination contre la COVID-19 par région. Ces données agrégées et anonymisées vous aident à comprendre ainsi qu'à comparer les besoins d'information des communautés. Nous publions ces données pour éclairer les efforts de santé publique en matière de confiance dans les vaccins. En savoir plus", 11 | "about_data_title": "À propos de ces données", 12 | "about_data_content": "Vous pouvez utiliser ces données pour comparer la popularité des recherches parmi les sujets liés à la vaccination contre la COVID-19 et voir quels sujets sont populaires ou en hausse. La valeur qui représente la popularité des recherches n'est pas un nombre absolu de recherches, mais une valeur représentant la popularité relative que nous mettons à l'échelle pour faciliter la comparaison des régions entre elles ou d'une même région au fil du temps. Si vous voulez en savoir plus sur notre calcul et notre traitement, consultez nos documents techniques.", 13 | "best_use_title": "Comment tirer le meilleur parti de ces données", 14 | "best_use_content": "

Nous avons utilisé la même normalisation et la même mise à l'échelle partout afin que vous puissiez faire ces comparaisons:

  • Comparez une région à d'autres pour voir où vous pourriez concentrer vos efforts.
  • Comparez une région au fil du temps pour voir comment les besoins en information de votre communauté ont changé ou pour voir les retombées de vos efforts de communication et des événements d'actualité.

N'oubliez pas que les données indiquent la popularité auprès du public, et non des opinions ou des événements réels. Vous ne pouvez pas conclure qu'une communauté souffre de nombreux effets secondaires à partir d'un intérêt accru pour la catégorie de la sécurité et des effets secondaires.

", 15 | "protecting_privacy_title": "Protéger la confidentialité", 16 | "proctecting_privacy_content": "

Nous avons développé les statistiques de recherche sur le vaccin pour rendre service tout en adhérant à nos rigoureux protocoles de confidentialité pour les données de recherche. Les requêtes de recherche individuelles ou d'autres informations d'ordre personnel ne sont jamais rendues disponibles. Pour ces données, nous utilisons la confidentialité différentielle, qui ajoute du bruit artificiel à nos données tout en permettant des résultats de haute qualité sans identifier une personne en particulier. De plus, nous n'affichons pas de données pour les régions dont l'étendue est inférieure à trois kilomètres2.

Pour en savoir plus sur les méthodes de confidentialité utilisées pour générer les données, veuillez lire la description du traitement d'anonymisation.

", 17 | "case_studies_title": "Études de cas", 18 | "availability_title": "Disponibilité et mises à jour", 19 | "availability_content": "

Pour télécharger ou utiliser les données ou les statistiques, vous devez accepter les conditions d'utilisation de Google.

Nous mettrons à jour les données chaque semaine. Vous pouvez vérifier les dates dans les graphiques pour consulter la journée la plus récente dans les données. Si vous téléchargez le fichier CSV, n'oubliez pas d'obtenir une version mise à jour chaque semaine.

Nous continuerons de mettre à jour ce produit tant que les experts en santé publique le trouveront utile dans leurs efforts de vaccination contre la COVID-19. Nos données publiées resteront accessibles au public pour soutenir la recherche et l'évaluation à long terme.

", 20 | "next_steps": { 21 | "query_dataset_title": "Interroger l'ensemble de données avec SQL", 22 | "query_dataset_content": "Obtenez des statistiques à l'aide de BigQuery de Google Cloud. Analysez avec SQL, générez des rapports ou appelez l'API à partir de votre code.", 23 | "query_dataset_link": "Ensemble de données publiques de BigQuery", 24 | "analyze_covariate_title": "Analysez avec des données de covariance", 25 | "analyze_covariate_content": "Analysez ces données avec d'autres covariables dans le référentiel de données ouvertes sur la COVID-19.", 26 | "analyze_covariate_link": "Référentiel GitHub", 27 | "feedback_title": "Parlez-nous de votre projet", 28 | "feedback_content": "Nous aimerions en savoir plus sur la façon dont vous utilisez les données. Envoyez des commentaires en utilisant notre formulaire.", 29 | "feedback_link": "Commentaires" 30 | }, 31 | "download_popup": { 32 | "terms": "Pour télécharger ou utiliser les données ou les statistiques, vous devez accepter les conditions d'utilisationde Google.", 33 | "download_dataset": "TÉLÉCHARGER DES ENSEMBLES DE DONNÉES", 34 | "countries": { 35 | "australia": "Australie", 36 | "canada": "Canada", 37 | "uk": "Royaume-Uni", 38 | "usa": "États-Unis", 39 | "ireland": "Irlande" 40 | } 41 | }, 42 | "top_queries": { 43 | "top_searches": "Recherches les plus fréquentes", 44 | "info": "Renseignements", 45 | "done": "Terminé", 46 | "rising": "En hausse", 47 | "related": "Requêtes connexes" 48 | }, 49 | "case_studies": { 50 | "title": "Études de cas", 51 | "pgp_title": "The Public Good Projects", 52 | "grapevine_title": "Grapevine Health", 53 | "pgp_desc": "Utiliser les statistiques de recherche de Google relatives au vaccin pour augmenter la confiance dans les vaccins parmi les communautés PANDC", 54 | "grapevine_desc": "Améliorer les renseignements sur les lieux à potentiel élevé pour la sensibilisation à la vaccination" 55 | } 56 | }, 57 | "navigation": { 58 | "download_data": "Télécharger les données", 59 | "documentation": "Documentation", 60 | "country_picker": "Sélectionner un pays", 61 | "map_interest": "Popularité", 62 | "footer": { 63 | "about": "À propos de Google", 64 | "products": "Produits Google", 65 | "privacy": "Confidentialité", 66 | "terms": "Conditions d'utilisation", 67 | "third_party_notices": "Avis tiers" 68 | } 69 | }, 70 | "hints": { 71 | "country_selector": "Sélectionnez un pays pour voir plus de statistiques", 72 | "loading_data": "Chargement des données en cours…", 73 | "click_to_drill": "Cliquez pour faire un zoom avant" 74 | }, 75 | "tooltips": { 76 | "learn_more": "En savoir plus", 77 | "parent_tooltip": "Cette catégorie parente inclut les recherches des deux autres sous-catégories.", 78 | "vaccine_preamble": "Popularité des recherches sur n'importe quel aspect de la vaccination contre la COVID-19. Par exemple, « quand puis-je recevoir le vaccin contre la covid » ou ", 79 | "intent_preamble": "Popularité des recherches sur l'admissibilité, la disponibilité et l'accessibilité en ce qui concerne les vaccins contre la COVID-19. Par exemple, « vaccin covid près de chez moi » ou ", 80 | "vaccine_country_example": { 81 | "CA": "traqueur de vaccins", 82 | "IE": "HSE covid vaccine", 83 | "GB": "NHS covid vaccine", 84 | "US": "cdc vaccine tracker", 85 | "AU": "covid vaccine" 86 | }, 87 | "intent_country_example": { 88 | "CA": "vaccin covid québec", 89 | "IE": "book HSE covid vaccine", 90 | "GB": "book NHS covid vaccine", 91 | "US": "safeway covid vaccine", 92 | "AU": "pfizer vaccine melbourne" 93 | }, 94 | "scaled_tooltip": "Une valeur mise à l'échelle que vous pouvez comparer entre les régions, les périodes ou les sujets.", 95 | "safety_tooltip": "Popularité des recherches sur l'innocuité et les effets secondaires des vaccins contre la COVID-19. Par exemple, « le vaccin contre la covid est-il sûr » ou « effets secondaires du vaccin pfizer ». Une valeur mise à l'échelle que vous pouvez comparer entre les régions et les périodes.", 96 | "vaccine_top_query": "Recherches les plus courantes sur n'importe quel aspect de la vaccination contre la COVID-19, répertoriées par ordre de fréquence.", 97 | "safety_top_query": "Recherches les plus courantes sur l'innocuité et les effets secondaires des vaccins contre la COVID-19, répertoriées par ordre de fréquence.", 98 | "intent_top_query": "Recherches les plus courantes sur l'admissibilité, la disponibilité et l'accessibilité en ce qui concerne les vaccins contre la COVID-19, répertoriées par ordre de fréquence." 99 | }, 100 | "legend": { 101 | "no_data": "Les données sont insuffisantes", 102 | "interest": "Popularité", 103 | "hi": "Haut", 104 | "lo": "Bas", 105 | "change": "Variation" 106 | }, 107 | "quote": { 108 | "start": "« ", 109 | "end": " »" 110 | }, 111 | "map_license": { 112 | "us": "Chart includes data from the US Census Bureau", 113 | "gb": "Chart includes data from the Office for National Statistics licensed under the Open Government Licence v.3.0", 114 | "ie": "Chart includes data from Ordnance Survey Ireland licensed under CC:BY 4.0", 115 | "ca": "Le graphique comprend des données adaptées à partir des fichiers de limites de 2016 de Statistique Canada. Cette utilisation ne constitue pas une approbation de ce produit par Statistique Canada.", 116 | "au": "Chart includes Administrative Boundaries from the Australian Bureau of Statistics licensed under Creative Commons Attribution 4.0 International license (CC BY 4.0) accessed March 31, 2022." 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /public/glue/glue-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/TopQueries.svelte: -------------------------------------------------------------------------------- 1 | 186 | 187 |
188 |
189 |
190 | 207 | 224 | 241 |
242 |
245 | handleInfoPopup(e, `#info-popup-${selectedListId}`)} 246 | > 247 | info 248 |
249 |
250 |
251 |
252 |
{$_('content.top_queries.top_searches')}
253 |
254 |
255 | {dateRange} 256 |
257 |
264 | arrow_back_ios 265 |
266 |
273 | arrow_forward_ios 276 |
277 |
278 |
    279 | {#if loading} 280 |
    {$_('hints.loading_data')}
    281 | {:else} 282 | {#each topQueriesList as query} 283 |
  • {query.query}
  • 284 | {:else} 285 |
    {$_('legend.no_data')}
    286 | {/each} 287 | {/if} 288 |
289 |
290 |
291 |
{$_('content.top_queries.rising')}
292 |
293 |
294 | {dateRange} 295 |
296 |
303 | arrow_back_ios 304 |
305 |
312 | arrow_forward_ios 315 |
316 |
317 |
    318 | {#if loading} 319 |
    {$_('hints.loading_data')}
    320 | {:else} 321 | {#each risingQueriesList as query} 322 |
  • {query.query}
  • 323 | {:else} 324 |
    {$_('legend.no_data')}
    325 | {/each} 326 | {/if} 327 |
328 |
329 |
330 |
331 | 332 | 333 |
334 |

335 | {covid_vaccination_button_title} 336 |

337 |

338 | {$_('tooltips.vaccine_top_query')} 339 |

340 |

341 | {$_('tooltips.parent_tooltip')} 342 |

343 |

344 | {$_('tooltips.learn_more')} 345 |

346 |
347 |
348 |

349 | {vaccination_intent_button_title} 350 |

351 |

352 | {$_('tooltips.intent_top_query')} 353 |

354 |

355 | {$_('tooltips.learn_more')} 356 |

357 |
358 |
359 |

360 | {safety_side_effects_button_title} 361 |

362 |

363 | {$_('tooltips.intent_top_query')} 364 |

365 |

366 | {$_('tooltips.learn_more')} 367 |

368 |
369 | -------------------------------------------------------------------------------- /src/App.svelte: -------------------------------------------------------------------------------- 1 | 164 | 165 | 166 | {$_('project_title')} 167 | 168 | 169 |
170 |
171 |
172 | 182 |
183 | 189 | 196 |
197 |
198 | 250 | {#if !placeId} 251 |
252 | 263 |
264 |
265 | {/if} 266 |
267 | 268 |
269 |
270 |

{$_('project_title')}

271 |

272 | { @html $_('content.app_overview', {values: { 273 | aboutUrl: "#about" 274 | }})} 275 |

276 | 277 | {#if placeId} 278 | 284 | {:else} 285 | { 288 | return t.trends.covid19_vaccination; 289 | }} 290 | /> 291 | {/if} 292 | 293 |

{$_('content.about_data_title')}

294 |

295 | {@html $_('content.about_data_content', {values: { 296 | aboutDataUrl: "https://storage.googleapis.com/gcs-public-datasets/COVID-19%20Vaccination%20Search%20Insights%20documentation.pdf" 297 | }})} 298 |

299 |

{$_('content.best_use_title')}

300 | {@html $_('content.best_use_content')} 301 |

{$_('content.case_studies_title')}

302 | 303 | 327 | 328 |

{$_('content.protecting_privacy_title')}

329 | {@html $_('content.proctecting_privacy_content', {values: { 330 | diffPrivacyUrl: "https://www.youtube.com/watch?v=FfAdemDkLsc&feature=youtu.be&hl=en", 331 | anonymizationUrl: "https://arxiv.org/abs/2107.01179"}})} 332 |

{$_('content.availability_title')}

333 | {@html $_('content.availability_content', {values: {tosUrl: "https://policies.google.com/terms"}})} 334 |
335 |
336 |

{$_('content.next_steps.query_dataset_title')}

337 |

338 | {$_('content.next_steps.query_dataset_content')} 339 |

340 |

341 |

{@html $_('content.next_steps.query_dataset_link', {values: {bigQueryUrl: "http://console.cloud.google.com/marketplace/product/bigquery-public-datasets/covid19-vaccination-search-insights"}})} 342 |

343 |
344 |
345 |

{$_('content.next_steps.analyze_covariate_title')}

346 |

347 | {$_('content.next_steps.analyze_covariate_content')} 348 |

349 |

{@html $_('content.next_steps.analyze_covariate_link', {values: {gitHubUrl: "https://github.com/GoogleCloudPlatform/covid-19-open-data"}})} 350 |

351 |
352 |
353 |

{$_('content.next_steps.feedback_title')}

354 |

355 | {$_('content.next_steps.feedback_content')} 356 |

357 |

{@html $_('content.next_steps.feedback_link', {values: {feedBackUrl: "https://google-health.force.com/s/form?type=VSIFeedbackForm"}})} 358 |

359 |
360 |
361 |
362 |
363 | 411 |
412 | 413 | 416 | -------------------------------------------------------------------------------- /src/Clusters.svelte: -------------------------------------------------------------------------------- 1 | 229 | 230 |
231 |
232 |
233 | 250 | 267 | 284 |
285 |
288 | handleInfoPopup(e, `#info-popup-${selectedListId}`)} 289 | > 290 | info 291 |
292 |
293 |
294 |
295 |
{$_('content.top_queries.top_searches')}
296 | {#if loading} 297 |
{$_('hints.loading_data')}
298 | {:else} 299 | {#if topQueriesList.length !== 0} 300 |
301 | 302 |
{$_('legend.interest')}
303 |
{$_('legend.change')}
304 |
305 | {/if} 306 | {#each topQueriesList as query} 307 |
308 |
309 |
310 | 311 | {query.members.length === 0 ? query.query: query.query + ","} 312 | 313 | {formatMembersList(query.members)} 314 |
315 |
316 |
317 | {(Math.round(query.sni * 100) / 100).toFixed(2)} 318 |
319 | {#if query.change === null || query.change === 0} 320 |
{formatChange(query.change)}
321 | {:else if query.change > 0} 322 |
323 | {formatChange(query.change)} 324 |
325 | {:else} 326 |
327 | {formatChange(query.change)} 328 |
329 | {/if} 330 |
331 | {:else} 332 |
{$_('legend.no_data')}
333 | {/each} 334 | {/if} 335 |
336 |
337 |
{$_('content.top_queries.rising')}
338 |
339 |
340 | {dateRange} 341 |
342 |
349 | arrow_back_ios 350 |
351 |
358 | arrow_forward_ios 361 |
362 |
363 | 364 | {#if loading} 365 |
{$_('hints.loading_data')}
366 | {:else} 367 | {#if risingQueriesList.length !== 0} 368 |
369 | 370 |
{$_('legend.interest')}
371 |
{$_('legend.change')}
372 |
373 | {/if} 374 | {#each risingQueriesList as query} 375 |
376 |
377 |
378 | 379 | {query.members.length === 0 ? query.query: query.query + ","} 380 | 381 | {formatMembersList(query.members)} 382 |
383 |
384 |
385 | {(Math.round(query.sni * 100) / 100).toFixed(2)} 386 |
387 | {#if query.change === null || query.change === 0} 388 |
{formatChange(query.change)}
389 | {:else if query.change > 0} 390 |
391 | {formatChange(query.change)} 392 |
393 | {:else} 394 |
395 | {formatChange(query.change)} 396 |
397 | {/if} 398 |
399 | {:else} 400 |
{$_('legend.no_data')}
401 | {/each} 402 | {/if} 403 |
404 |
405 |
406 | 407 | 408 |
409 |

410 | {covid_vaccination_button_title} 411 |

412 |

413 | {$_('tooltips.vaccine_top_query')} 414 |

415 |

416 | {$_('tooltips.parent_tooltip')} 417 |

418 |

419 | {$_('tooltips.learn_more')} 420 |

421 |
422 |
423 |

424 | {vaccination_intent_button_title} 425 |

426 |

427 | {$_('tooltips.intent_top_query')} 428 |

429 |

430 | {$_('tooltips.learn_more')} 431 |

432 |
433 |
434 |

435 | {safety_side_effects_button_title} 436 |

437 |

438 | {$_('tooltips.intent_top_query')} 439 |

440 |

441 | {$_('tooltips.learn_more')} 442 |

443 |
444 | -------------------------------------------------------------------------------- /src/TrendsOverview.svelte: -------------------------------------------------------------------------------- 1 | 231 | 232 |
233 |
234 | 255 |
256 |
257 | 258 |
259 |
260 | 275 | 290 | 305 |
306 | 307 |
308 |
Region Name
309 |
{$_("legend.interest")}
310 |
311 |
312 | 313 | 314 | 315 |
316 |
317 | {covid_vaccination_title} 318 |
319 |
323 |
324 | 325 | 326 | 327 |
328 |
329 | {vaccination_intent_title} 330 |
331 |
335 |
336 | 337 | 338 | 339 |
340 |
341 | {safety_side_effects_title} 342 |
343 |
347 |
348 |
349 | 352 | {$_("hints.click_to_drill")} 353 |
354 |
355 | 356 | 357 | 358 | 359 |
360 |
361 |
{$_("legend.interest")}
362 |
363 |
364 | 365 |
366 |
367 | 368 | 369 | 370 |
371 |
372 |
373 | {$_("legend.no_data")} 374 |
375 |
376 |
377 |
378 | 379 | 380 | 381 |
382 |
383 |
{ 386 | handleInfoPopup(e, selectMapInfoPopup()); 387 | }} 388 | > 389 | info 390 |
391 |
392 |
393 |
394 |
395 | 396 |
397 |
{ 401 | decrementMapDate("#date-nav-button-back"); 402 | }} 403 | > 404 | arrow_back_ios 405 |
406 |
{ 410 | incrementMapDate("#date-nav-button-forward"); 411 | }} 412 | > 413 | arrow_forward_ios 414 |
415 |
416 |
417 | 418 | 419 |
420 | {#if !isMapInitialized} 421 |
{$_("hints.loading_data")}.
422 | {/if} 423 | 424 |
425 |

426 | {#if selectedCountryMetadata} 427 | {$_(`map_license.${selectedCountryMetadata.shapeFileLegal}`)} 428 | {/if} 429 |

430 |
431 |
432 | 433 | 434 |
435 |

436 | {covid_vaccination_title} 437 |

438 |

439 | {vaccineTooltip} 440 |

441 |

442 | {$_("tooltips.parent_tooltip")} 443 |

444 |

445 | {$_("tooltips.learn_more")} 446 |

447 |
448 |
449 |

450 | {vaccination_intent_title} 451 |

452 |

453 | {intentTooltip} 454 |

455 |

456 | {$_("tooltips.learn_more")} 457 |

458 |
459 |
460 |

461 | {safety_side_effects_title} 462 |

463 |

464 | {safetyTooltip} 465 |

466 |

467 | {$_("tooltips.learn_more")} 468 |

469 |
470 | {#if $regionalTrends.size > 0} 471 | { 476 | return t.trends.covid19_vaccination; 477 | }} 478 | title={covid_vaccination_title} 479 | {selectedCountryMetadata} 480 | > 481 |

482 | {vaccineTooltip} 483 |

484 |

485 | {$_("tooltips.parent_tooltip")} 486 |

487 |
488 | { 493 | return t.trends.vaccination_intent; 494 | }} 495 | title={vaccination_intent_title} 496 | {selectedCountryMetadata} 497 | > 498 |

499 | {intentTooltip} 500 |

501 |
502 | { 507 | return t.trends.safety_side_effects; 508 | }} 509 | title={safety_side_effects_title} 510 | {selectedCountryMetadata} 511 | > 512 |

513 | {safetyTooltip} 514 |

515 |
516 | {/if} 517 | 518 | {#if selectedCountryMetadata} 519 | 520 | {#if ["US", "AU", "GB", "IE", "CA"].includes(selectedCountryMetadata.countryCode) } 521 | 531 | {/if} 532 | {/if} 533 | 534 | 535 | 536 | 537 |
538 | -------------------------------------------------------------------------------- /src/CountryPicker.svelte: -------------------------------------------------------------------------------- 1 | 517 | 518 |
519 |
520 |