index.html (484 lines of code) (raw):
<!--
Copyright 2021 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Google Cloud region picker</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Tool to help you pick a Google Cloud region considering carbon footprint, price and latency.">
<meta name="theme-color" content="#1c74e9">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🗺️</text></svg>">
<!-- pre-load resources -->
<link rel="modulepreload" href="region-optimizer.js">
<link rel="preload" as="fetch" crossorigin="anonymous" href="data/regions.json">
<link rel="preload" as="fetch" crossorigin="anonymous" href="data/countries.json">
<link rel="preload" as="fetch" crossorigin="anonymous" href="data/prices.json">
<!-- Carbon info is from GitHub Pages -->
<link rel="preconnect" href="https://googlecloudplatform.github.io/">
<!-- Flags are from wikipedia-->
<link rel="preconnect" href="https://upload.wikimedia.org">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Google Cloud region picker">
<meta name="twitter:description" content="Tool to help you pick a Google Cloud region considering carbon footprint, price and latency.">
<meta name="twitter:image" content="https://cloud.withgoogle.com/region-picker/images/gcp-region-picker.png">
<meta property="og:type" content="profile">
<meta property="og:title" content="Google Cloud region picker">
<meta property="og:description" content="Tool to help you pick a Google Cloud region considering carbon footprint, price and latency.">
<meta property="og:url" content="https://cloud.withgoogle.com/region-picker">
<meta property="og:image" content="https://cloud.withgoogle.com/region-picker/images/gcp-region-picker.png">
<script>
// Serve either at the root of a domain name, or at a subpath with a trailing slash.
// Doesn't work at a subpath without a trailing slash (issue #7).
let p = window.location.pathname;
if( p.length>=1 && p.slice(-1) !== '/' && p.slice(-10) !== 'index.html' ) {
// Then redirect
window.location.href += '/';
}
</script>
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "SoftwareApplication",
"name": "Google Cloud region picker",
"abstract": "Tool to help you pick a Google Cloud region considering carbon footprint, price and latency.",
"url": "https://cloud.withgoogle.com/region-picker",
"image": "https://cloud.withgoogle.com/region-picker/images/gcp-region-picker.png"
}
</script>
<style>
/*
* The following is manually copied from the stylesheet of the official Google Fonts API
* https://fonts.googleapis.com/css2?family=Google+Sans:wght@400&display=optional
* It's unclear if it the src URL is stable.
*/
@font-face {
font-family: 'Google Sans';
font-style: normal;
font-weight: 400;
/* Use optional if we do not tolerate any Layout shift */
/* font-display: optional; */
font-display: swap;
src: url(https://fonts.gstatic.com/s/googlesans/v27/4UaGrENHsxJlGDuGo1OIlL3Owp4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
:root {
--base-width: 35rem;
--base-unit: 24px;
--half-unit: calc(var(--base-unit) / 2);
--rounder-corner: 8px;
--blue-500: #1a73e8;
--shadow-card: 0px 4px 12px rgba(0, 0, 0, 0.15);
}
html {
font-family: 'Google Sans', Arial, sans-serif;
line-height: 1.4;
height: 100%;
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
h1, h2, h3 {
font-style: normal;
font-weight: normal;
}
h1{
font-size: 2.5em;
}
h2{
font-size: 1.25em;
}
h3{
font-size: 1em;
}
h1 .accent, h2 {
color: var(--blue-500)
}
a, a:visited, a:hover{
color:black;
}
a:hover, a:active{
outline-width:0;
}
body {
font-size: 1rem;
margin: 0;
flex-direction: column;
display: flex;
justify-content: space-between;
height: 100%;
align-items: stretch;
}
header {
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.05);
height: 80px; /** not so sure why I need to use such big height to get */
padding: var(--base-unit);
background-origin: content-box;
background-repeat: no-repeat;
background-image: url(images/cloud-logo.svg);
}
main {
display: flex;
flex: 1 0;
flex-wrap: wrap;
justify-content: space-evenly;
}
footer {
background-color: #F1F3F4;
color: #5F6368;
font-size: 0.8rem;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: var(--base-unit);
}
.group {
width: 50%;
padding: var(--base-unit);
}
.group .content {
max-width: var(--base-width);
margin-left: auto;
margin-right: auto;
}
.group.regions {
background-color: var(--blue-500);
}
.group.regions,
.group.regions h2 {
color: white;
}
#share {
opacity: 0;
}
select {
display:block;
width: 100%;
padding: calc(var(--base-unit) / 2) var(--base-unit);
border: none;
background-color: #F8F9FA;
}
label:not(:first-child){
margin-top:1em;
}
p label{
display:inline;
}
p label + label{
margin-left:1em;
}
select, input[type], textarea{
margin-bottom:1em;
}
input[type=checkbox], input[type=radio]{
margin-bottom:0;
}
input.weight {
width: calc(var(--base-width) / 2.5);
}
.zero {
opacity: 0.4;
}
button {
border: none;
background-color: transparent;
}
button.icon-button {
background-repeat: no-repeat;
height: var(--base-unit);
width: var(--base-unit);
font-size: 0;
}
#share {
float: right;
background-image: url(images/icons/share-24px.svg);
}
.help {
width: 1em;
height: 1em;
}
/*
* Remove default styling for <details> tag.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details#customizing_the_disclosure_widget
*/
details > summary {
list-style: none;
}
.explanation {
font-size: small;
}
.params {
box-shadow: var(--shadow-card);
border-radius: var(--rounder-corner);
background-color: white;
padding: calc(2*var(--base-unit));
}
.weight-info {
color: hsl(0 0% 50%)
}
.region-code {
font-family: monospace;
}
.weight-group {
background-repeat: no-repeat;
padding-left: calc(24px + 6px);
}
.weight-group.carbon {
background-image: url(images/icons/gleaf_black_24dp.svg);
}
.weight-group.price {
background-image: url(images/icons/attach_money-24px.svg);
}
.weight-group.latency {
background-image: url(images/icons/timer-24px.svg);
}
.weight-group.products {
background-image: url(images/icons/checkbox-24px.svg);
}
#results {
padding: 0;
list-style-position: inside;
}
#results > li {
color: black;
background: #FFFFFF;
box-shadow: var(--shadow-card);
border-radius: var(--rounder-corner);
padding: var(--base-unit);
margin: var(--base-unit);
min-height: calc(6 * var(--base-unit));
}
#results.short li:nth-child(6),
#results.short li:nth-child(7),
#results.short li:nth-child(8),
#results.short li:nth-child(9),
#results.short li:nth-child(10) {
display: none;
}
#results > li details {
width: calc(100% - 2rem);
display: inline-flex;
cursor: pointer;
}
#results > li ul {
padding: 0;
}
#results > li summary {
display: inline-flex;
justify-content: space-between;
align-items: center;
}
#results > li .name-group {
display: inline-flex;
align-items: center;
}
#results > li .flag {
width: calc(2 * var(--base-unit));
height: calc(2 * var(--base-unit));
object-fit: contain;
margin: var(--base-unit);
}
#results > li .symbols {
width: calc(4 * var(--base-unit));
}
/* Show / Hide leaves or dollars*/
#results > li .symbols.n0 img,
#results > li .symbols.n1 img:nth-child(2),
#results > li .symbols.n1 img:nth-child(3),
#results > li .symbols.n2 img:nth-child(3) {
opacity: 0.1;
}
#more {
color: white;
margin: 0 auto;
display: block;
border-width: 1px;
border-style: solid;
border-radius: var(--rounder-corner);
padding: var(--half-unit);
width: 25%;
}
#missing-data-warning {
float: right;
}
#missing-data-regions span {
margin-right: 10px;
}
@media (max-width: 50rem) {
main {
display: block;
}
.params {
padding: var(--half-unit);
}
input[type=range] {
/** TODO: do not hardcode 6 */
width: calc(var(--base-width) / 6);
}
.group {
width: 100%;
margin-left: auto;
margin-right: auto;
}
.weight-info {
font-size: small;
}
#share {
display: block;
opacity: 100;
}
#results > li {
padding: var(--half-unit);
margin-left: auto;
margin-right: auto;
min-height: calc(3 * var(--base-unit));
}
#results > li .flag {
width: var(--base-unit);
height: var(--base-unit);
margin: var(--half-unit);
}
#results > li .symbols {
width: calc(2 * var(--base-unit));
}
#results > li .symbols img {
width: var(--half-unit);
}
}
</style>
</head>
<body>
<header>
<button class="icon-button" id="share">Share</button>
</header>
<main>
<div class="group">
<div class="content">
<h1>
Google Cloud
<br/>
<span class="accent">Region Picker</span>
</h1>
<p>This tool helps you pick a Google Cloud region considering approximated carbon footprint, price and latency.</p>
<p>This is not an officially supported Google product and does not cover all <a href="https://cloud.google.com/about/locations">Google Cloud locations</a>. Data last updated on 2024-07-04.</p>
<form class="params">
<h2>Optimize for</h2>
<div class="weight-group carbon">
<details>
<summary><label for="carbon">Lower carbon footprint</label> <img src="images/icons/help_outline_black_24dp.svg" class="help" alt="A question mark" width="16" height="16" /></summary>
<p class="explanation">
Approximated using the yearly carbon free energy percentage and average electricity carbon intensity of the region (<a href="https://cloud.google.com/sustainability/region-carbon">source</a>).
<br/>
Google <a href="https://cloud.google.com/sustainability">invests</a> in enough renewable energy and carbon offsets to neutralize the global operational carbon emissions footprint of Google Cloud per the GHG protocol under the Scope 2 market-based methodology.
</p>
</details>
<span class="weight-info">Not important</span>
<input type="range" id="carbon" name="carbon" min="0" max="10" class="weight">
<span class="weight-info">Important</span>
</div>
<div class="weight-group price">
<details>
<summary><label for="price">Lower price</label> <img src="images/icons/help_outline_black_24dp.svg" class="help" alt="A question mark" width="16" height="16"/></summary>
<p class="explanation">Approximated using <a href="https://cloud.google.com/compute/all-pricing#e2_machine-types">Google Compute Engine E2 instance vCPU price</a>.</p>
</details>
<span class="weight-info">Not important</span>
<input type="range" id="price" name="price" min="0" max="10" class="weight">
<span class="weight-info">Important</span>
</div>
<div class="weight-group latency">
<details>
<summary><label for="latency" class="weight-title">Lower latency</label> <img src="images/icons/help_outline_black_24dp.svg" class="help" alt="A question mark" width="16" height="16"/></summary>
<p class="explanation">
Approximated using physical distance between selected countries and the city or country of the region.
This isn't indicative of actual user latency on Google's backbone for any Google Cloud product.
For Google Cloud to internet latencies, please use <a href="https://cloud.google.com/network-intelligence-center/docs/performance-dashboard/concepts/overview#traffic-between-google-cloud-and-internet-google-cloud-view">Network Intelligence Center</a> available in the <a href="https://console.cloud.google.com/net-intelligence/performance/global-dashboard/latency">Cloud Console</a>.
</p>
</details>
<span class="weight-info">Not important</span>
<input type="range" id="latency" name="latency" min="0" max="10" class="weight">
<span class="weight-info">Important</span>
<div id="locations-group">
<label for="locations">Where is your traffic coming from?</label>
<select multiple id="locations" name="locations">
<option value="--current-location--" selected>Your current location</option>
</select>
</div>
</div>
<div class="weight-group products">
<details>
<summary><label for="products" class="weight-title">Product availability</label> <img src="images/icons/help_outline_black_24dp.svg" class="help" alt="A question mark" width="16" height="16"/></summary>
<p class="explanation">
Only regions where selected products are available will be suggested.
This list doesn't include <a href="https://cloud.google.com/about/locations#global-products">global products</a>.
Product availability is quickly changing, this tool is likely to not reflect the latest availability data.
For an official and up to date table of product regional availability, please refer to <a href="https://cloud.google.com/about/locations#regions">the official Google Cloud website</a>.
</p>
</details>
<select multiple id="products" name="products"></select>
</div>
</form>
</div>
</div>
<div class="group regions">
<div class="content">
<div id="missing-data-warning" hidden>
Data missing for <span id="missing-data-regions"></span>
</div>
<h2>Recommended regions</h2>
<ol id="results" class="short">
<!-- Keep placeholders results to avoid layout shift -->
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
<button id="more">More</button>
</div>
</div>
</main>
<footer>
<span class="links">
<a href="https://cloud.google.com/sustainability">Google Cloud sustainability</a>
</span>
<span class="disclaimer">
<a href="https://github.com/GoogleCloudPlatform/region-picker">Contribute or report issues</a>
</span>
</footer>
<template id="result-row">
<li>
<details>
<summary class="list-content">
<div class="name-group">
<img class="flag" alt="flag" width="48" height="48"/>
<div>
<span class="region region-code"></span>
<br/>
<span class="name"></span>
</div>
</div>
<div class="symbols-group">
<div class="symbols leaves">
<img crossorigin="anonymous" referrerpolicy="no-referrer" src="images/icons/gleaf_black_24dp.svg" alt="leaf" width="24" height="24"/>
<img crossorigin="anonymous" referrerpolicy="no-referrer" src="images/icons/gleaf_black_24dp.svg" alt="leaf" width="24" height="24"/>
<img crossorigin="anonymous" referrerpolicy="no-referrer" src="images/icons/gleaf_black_24dp.svg" alt="leaf" width="24" height="24"/>
</div>
<div class="symbols dollars">
<img crossorigin="anonymous" referrerpolicy="no-referrer" src="images/icons/attach_money-24px.svg" alt="dollar" width="24" height="24"/>
<img crossorigin="anonymous" referrerpolicy="no-referrer" src="images/icons/attach_money-24px.svg" alt="dollar" width="24" height="24"/>
<img crossorigin="anonymous" referrerpolicy="no-referrer" src="images/icons/attach_money-24px.svg" alt="dollar" width="24" height="24"/>
</div>
</div>
</summary>
<div>
<ul>
<li class="cfe-sentence">Carbon Free Energy: <span class="cfe"></span>%</li>
<li>Grid carbon intensity: <span class="gCO2_kWh"></span> gCO2eq/kWh</li>
<li>Google Compute Engine price: $<span class="price"></span> / vCPU-hour</li>
</ul>
</div>
</details>
</li>
</template>
<script type="module" src="index.js"></script>
</body></html>