initial cleanup

This commit is contained in:
terraputix
2026-01-07 23:45:10 +01:00
parent 2d31df88f0
commit f781ef4fd6
325 changed files with 1903 additions and 1000 deletions
+8 -1
View File
@@ -23,7 +23,14 @@ export default defineConfig(
rules: {
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
'no-undef': 'off'
'no-undef': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}
]
}
},
{
+140 -131
View File
@@ -1,14 +1,13 @@
<script lang="ts">
import { createEventDispatcher, onDestroy } from 'svelte';
import { type GeoLocation } from '$lib/stores/settings';
import * as Alert from '$lib/components/ui/alert';
import { Button } from '$lib/components/ui/button';
import * as Dialog from '$lib/components/ui/dialog';
import { Input } from '$lib/components/ui/input';
export let label: string = 'Search Locations...';
export let placeholder: string = 'Enter city name...';
interface ResultSet {
results: GeoLocation[] | undefined;
@@ -80,7 +79,6 @@
};
}
// Always set format=json to fetch data
const url = 'https://geocoding-api.open-meteo.com/v1/search';
const fetchUrl = `${url}?${new URLSearchParams({ name: searchQuery })}`;
const result = await fetch(fetchUrl);
@@ -97,169 +95,180 @@
<Dialog.Root bind:open={dialogOpen}>
<Dialog.Trigger
id="location_search"
class="group flex h-14 w-full cursor-pointer items-center justify-start rounded-xl border-2 border-gray-200 bg-white px-6 transition-all duration-200 hover:border-blue-400 hover:shadow-md focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 dark:border-gray-600 dark:bg-gray-700"
onclick={(e) => {
e.preventDefault();
dialogOpen = !dialogOpen;
}}
class="flex h-12 w-full cursor-pointer items-center justify-center rounded-md border border-border px-5 pr-6 duration-200 hover:bg-accent"
><svg
class="lucide lucide-search mr-[5px]"
>
<svg
class="mr-3 h-5 w-5 text-gray-400 transition-colors group-hover:text-blue-500"
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.3-4.3" />
</svg>
{label}</Dialog.Trigger
>
<span
class="text-gray-600 transition-colors group-hover:text-gray-900 dark:text-gray-300 dark:group-hover:text-white"
>
{label}
</span>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay class="bg-black/5" />
<Dialog.Overlay class="bg-black/20 backdrop-blur-sm" />
<Dialog.Content
class="top-[10%] flex max-h-[calc(100vh-10%)] min-h-[400px] translate-y-0 flex-col overflow-y-auto border-border sm:max-w-[600px]"
class="top-[10%] flex max-h-[calc(100vh-10%)] min-h-[500px] translate-y-0 flex-col overflow-hidden rounded-2xl border-border bg-white shadow-2xl sm:max-w-[700px] dark:bg-gray-800"
>
<Dialog.Header>
<Dialog.Title>Search Locations</Dialog.Title>
<Dialog.Header class="pb-6">
<Dialog.Title class="text-center text-2xl font-bold">Find Your Location</Dialog.Title>
<p class="text-center text-gray-600 dark:text-gray-300">
Search for a city or use GPS to detect your location
</p>
</Dialog.Header>
<div>
<div class="flex gap-3">
<Input
type="search"
id="location_search_input"
autocomplete="off"
spellcheck="false"
aria-label="Search Location"
bind:value={searchQuery}
/>
<Button
id="location_search_gps"
variant="outline"
title="Detect Location via GPS"
onclick={() => (searchQuery = 'GPS')}
><svg
class="lucide lucide-mouse-pointer-2"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z"
<div class="flex-1 overflow-hidden">
<div class="px-6">
<div class="mb-6 flex gap-3">
<div class="flex-1">
<Input
type="search"
{placeholder}
class="h-12 text-lg"
autocomplete="off"
spellcheck="false"
aria-label="Search Location"
bind:value={searchQuery}
/>
</svg></Button
>
</div>
{#await results}
<div class="mt-4 flex h-full items-center justify-center">
<Alert.Root class="my-auto w-[unset] border-border">
<Alert.Description>Loading...</Alert.Description>
</Alert.Root>
</div>
<Button
variant="outline"
size="lg"
class="px-4"
title="Use GPS Location"
onclick={() => (searchQuery = 'GPS')}
>
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
</Button>
</div>
{:then results}
{#if results.results && results.results.length === 0}
{#if searchQuery.length < 2}
<Alert.Root class="my-auto mt-4 w-[unset] border-border">
<Alert.Description>Start typing to search for locations</Alert.Description>
</Alert.Root>
{:else}
<Alert.Root class="my-auto !mt-4 w-[unset] border-border">
</div>
<div class="flex-1 overflow-y-auto px-6 pb-6">
{#await results}
<div class="flex h-32 items-center justify-center">
<div class="flex items-center space-x-3">
<div class="h-6 w-6 animate-spin rounded-full border-b-2 border-blue-600"></div>
<span class="text-gray-600 dark:text-gray-300">Searching...</span>
</div>
</div>
{:then results}
{#if results.results && results.results.length === 0}
{#if searchQuery.length < 2}
<Alert.Root class="border-blue-200 bg-blue-50 dark:bg-blue-900/20">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<Alert.Description class="text-blue-700 dark:text-blue-300">
Start typing to search for locations or use GPS to detect your current position
</Alert.Description>
</Alert.Root>
{:else}
<Alert.Root class="border-orange-200 bg-orange-50 dark:bg-orange-900/20">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.728-.833-2.498 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
/>
</svg>
<Alert.Description class="text-orange-700 dark:text-orange-300">
No locations found for "{searchQuery}". Try a different search term.
</Alert.Description>
</Alert.Root>
{/if}
{:else if !results.results}
<Alert.Root variant="destructive">
<Alert.Description>No locations found</Alert.Description>
</Alert.Root>
{/if}
{:else if !results.results}
<Alert.Root class="my-auto !mt-4 w-[unset] border-border">
<Alert.Description>No locations found</Alert.Description>
</Alert.Root>
{:else}
<div class="list-group mt-4">
<div id="location_search_results" class="rounded-lg border border-border">
{:else}
<div class="space-y-2">
{#each results.results || [] as location, i (i)}
<Button
variant="outline"
class="location-search-result flex h-[unset] w-full justify-between gap-0 rounded-none py-2 pr-1 pl-3 not-last:border-b md:pr-2 {i ===
0
? 'rounded-t-md'
: ''} {results.results && i === results.results.length - 1
? 'rounded-b-md'
: ''}"
<button
class="group w-full rounded-xl border border-gray-200 p-4 text-left transition-all duration-200 hover:border-blue-400 hover:bg-blue-50 dark:border-gray-600 dark:hover:bg-blue-900/20"
onclick={() => selectLocation(location)}
>
<div class="pointer-events-none flex flex-col gap-1 truncate">
<div class="flex items-center gap-2 truncate text-lg">
<div class="flex items-center justify-between">
<div class="flex flex-1 items-center space-x-4">
<img
height="24"
width="24"
class="h-10 w-10 rounded-full shadow-md"
src="/images/country-flags/{(
location.country_code || 'united_nations'
).toLowerCase()}.svg"
title={location.country}
alt={location.country_code}
alt={location.country}
/>
{location.name}
<div class="flex-1">
<h3
class="font-semibold text-gray-900 group-hover:text-blue-600 dark:text-white dark:group-hover:text-blue-400"
>
{location.name}
</h3>
<p class="text-sm text-gray-600 dark:text-gray-300">
{location.admin1 || ''}
{location.country || ''}
</p>
<p class="text-xs text-gray-500 dark:text-gray-400">
{location.latitude.toFixed(2)}°N {location.longitude.toFixed(2)}°E
{#if location.elevation}{location.elevation.toFixed(0)}m{/if}
</p>
</div>
</div>
<div class="truncate text-left text-sm text-muted-foreground">
{location.admin1 || ''} ({location.latitude.toFixed(2)}°N {location.longitude.toFixed(
2
)}°E{#if location.elevation}{' ' +
location.elevation.toFixed(0) +
'm asl'}{/if})
</div>
</div>
<div class="-mr-1 flex justify-self-end">
<Button
variant="ghost"
class="px-2 duration-200 hover:brightness-[140%] md:px-3"
href="https://www.openstreetmap.org/#map=13/{location.latitude}/{location.longitude}"
target="_blank"
title="Show on map"
onclick={(e) => {
e.stopPropagation();
}}
<svg
class="h-5 w-5 text-gray-400 group-hover:text-blue-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<svg
class="lucide lucide-map"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.2"
<path
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z"
/>
<path d="M15 5.764v15" />
<path d="M9 3.236v15" />
</svg>
</Button>
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</div>
</Button>
</button>
{/each}
</div>
</div>
{/if}
{:catch error}
<Alert.Root variant="destructive" class="my-auto mt-4 w-[unset]">
<Alert.Description>{error.message}</Alert.Description>
</Alert.Root>
{/await}
{/if}
{:catch error}
<Alert.Root variant="destructive">
<Alert.Description>Error: {error.message}</Alert.Description>
</Alert.Root>
{/await}
</div>
</div>
</Dialog.Content>
</Dialog.Portal>
@@ -0,0 +1,142 @@
<script lang="ts">
import { page } from '$app/stores';
import { Button } from '$lib/components/ui/button';
import { resolve } from '$app/paths';
const links = [
{
title: 'Current Weather',
url: '/weather/week',
description: 'Current conditions and overview',
icon: '🌡️'
},
{
title: 'Model Comparison',
url: '/weather/compare',
description: 'Compare multiple weather models',
icon: '📊'
},
{
title: '14 Day Forecast',
url: '/weather/14-day',
description: 'Extended forecast with uncertainty',
icon: '📅'
}
];
let mobileNavOpened = $state(false);
let currentPath = $derived($page.url.pathname);
const isActive = (url: string) => {
return currentPath === url || currentPath.startsWith(url + '/');
};
</script>
<nav
class="sticky top-0 z-50 border-b border-gray-200/50 bg-white/90 shadow-sm backdrop-blur-lg dark:border-gray-700/50 dark:bg-gray-900/90"
>
<div class="container mx-auto px-6">
<div class="flex h-16 items-center justify-between">
<!-- Logo -->
<div class="flex items-center space-x-3">
<div class="rounded-lg bg-gradient-to-r from-blue-600 to-purple-600 p-2">
<svg class="h-6 w-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"
/>
</svg>
</div>
<a
href={resolve('/')}
class="text-xl font-bold text-gray-900 transition-colors hover:text-blue-600 dark:text-white"
>
Open-Meteo Weather
</a>
</div>
<!-- Desktop Navigation -->
<div class="hidden items-center space-x-2 md:flex">
{#each links as link (link.title)}
<Button
href={link.url}
variant={isActive(link.url) ? 'default' : 'ghost'}
size="sm"
class="group relative px-4 py-2 {isActive(link.url)
? 'bg-blue-600 text-white shadow-md'
: 'hover:bg-blue-50 dark:hover:bg-blue-900/20'}"
>
<span class="mr-1">{link.icon}</span>
{link.title}
</Button>
{/each}
</div>
<!-- Mobile menu button -->
<div class="md:hidden">
<Button
variant="ghost"
size="sm"
class="rounded-lg"
onclick={() => (mobileNavOpened = !mobileNavOpened)}
>
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{#if mobileNavOpened}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
{:else}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
/>
{/if}
</svg>
</Button>
</div>
</div>
<!-- Mobile Navigation -->
{#if mobileNavOpened}
<div class="border-t border-gray-200 py-4 md:hidden dark:border-gray-700">
<div class="space-y-2">
{#each links as link (link.title)}
<Button
href={link.url}
variant={isActive(link.url) ? 'default' : 'ghost'}
class="w-full justify-start py-3 {isActive(link.url) ? 'bg-blue-600 text-white' : ''}"
onclick={() => (mobileNavOpened = false)}
>
<div class="flex items-center space-x-3">
<span class="text-lg">{link.icon}</span>
<div class="text-left">
<div class="font-medium">{link.title}</div>
<div
class="text-xs {isActive(link.url)
? 'text-blue-100'
: 'text-gray-500 dark:text-gray-400'}"
>
{link.description}
</div>
</div>
</div>
</Button>
{/each}
</div>
</div>
{/if}
</div>
</nav>
<style>
:global(.container) {
max-width: 1200px;
}
</style>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-action"
class={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,15 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div bind:this={ref} data-slot="card-content" class={cn("px-6", className)} {...restProps}>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
</script>
<p
bind:this={ref}
data-slot="card-description"
class={cn("text-muted-foreground text-sm", className)}
{...restProps}
>
{@render children?.()}
</p>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-footer"
class={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-header"
class={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className
)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-title"
class={cn("leading-none font-semibold", className)}
{...restProps}
>
{@render children?.()}
</div>
+23
View File
@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card"
class={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className
)}
{...restProps}
>
{@render children?.()}
</div>
+25
View File
@@ -0,0 +1,25 @@
import Root from "./card.svelte";
import Content from "./card-content.svelte";
import Description from "./card-description.svelte";
import Footer from "./card-footer.svelte";
import Header from "./card-header.svelte";
import Title from "./card-title.svelte";
import Action from "./card-action.svelte";
export {
Root,
Content,
Description,
Footer,
Header,
Title,
Action,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
Action as CardAction,
};
+3 -2
View File
@@ -3,7 +3,7 @@ import { writable } from 'svelte/store';
// Placeholder function for urlHashStore
// In a real application, this would handle URL hash parameters
// and return a Svelte store that reflects those parameters.
export function urlHashStore(initialValue: Record<string, any>) {
export function urlHashStore(initialValue: Record<string, unknown>) {
const { subscribe, set, update } = writable(initialValue);
// In a full implementation, you would add logic here to:
@@ -18,6 +18,7 @@ export function urlHashStore(initialValue: Record<string, any>) {
set,
update,
// You might want to add methods to easily update specific hash parameters
updateParam: (key: string, value: any) => update((current) => ({ ...current, [key]: value }))
updateParam: (key: string, value: unknown) =>
update((current) => ({ ...current, [key]: value }))
};
}
+9 -2
View File
@@ -5,5 +5,12 @@
let { children } = $props();
</script>
<svelte:head><link rel="icon" href={favicon} /></svelte:head>
{@render children()}
<svelte:head>
<link rel="icon" href={favicon} />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</svelte:head>
<main class="min-h-screen">
{@render children()}
</main>
+266 -6
View File
@@ -1,8 +1,268 @@
<script>
import { base } from '$app/paths';
<script lang="ts">
import { onMount } from 'svelte';
import { fade, fly } from 'svelte/transition';
import { Button } from '$lib/components/ui/button';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '$lib/components/ui/card';
import { storedLocation } from '$lib/stores/settings';
import { get } from 'svelte/store';
let mounted = $state(false);
let location = get(storedLocation);
onMount(() => {
mounted = true;
});
const features = [
{
title: 'Week Prediction',
description:
'Get detailed hourly weather forecasts for the next 7 days with interactive charts and temperature gradients.',
href: `/weather`,
icon: 'calendar',
gradient: 'from-blue-500 to-cyan-500'
},
{
title: 'Model Comparison',
description:
'Compare multiple weather models side-by-side to understand forecast uncertainty and accuracy.',
href: '/weather/compare',
icon: 'trending-up',
gradient: 'from-purple-500 to-pink-500'
},
{
title: '14 Day Weather',
description:
'Extended forecast with ensemble model spreads showing temperature ranges and uncertainty.',
href: '/weather/14-day',
icon: 'cloud',
gradient: 'from-green-500 to-blue-500'
}
];
</script>
<h1>Open-Meteo Weather</h1>
<p>
<a href="{base}/weather">Weather Forecast</a>
</p>
<svelte:head>
<title>Open-Meteo Weather - Advanced Weather Forecasting</title>
<meta
name="description"
content="Professional weather forecasting with multiple models, extended forecasts, and detailed comparisons. Powered by Open-Meteo API."
/>
</svelte:head>
<div
class="min-h-screen bg-gradient-to-br from-blue-50 via-white to-cyan-50 dark:from-gray-900 dark:via-gray-800 dark:to-blue-900"
>
<!-- Hero Section -->
<div class="container mx-auto px-6 pt-20 pb-16">
{#if mounted}
<div class="mb-16 text-center" in:fade={{ duration: 800, delay: 200 }}>
<h1
class="mb-6 bg-gradient-to-r from-blue-600 via-purple-600 to-cyan-600 bg-clip-text text-5xl font-bold text-transparent md:text-7xl"
>
Open-Meteo Weather
</h1>
<p class="mx-auto mb-8 max-w-3xl text-xl text-gray-600 md:text-2xl dark:text-gray-300">
Professional weather forecasting with advanced models, detailed comparisons, and extended
predictions
</p>
<!-- Quick Access Button -->
<div in:fly={{ y: 20, duration: 600, delay: 600 }}>
<Button
href="/weather"
size="lg"
class="bg-gradient-to-r from-blue-600 to-purple-600 px-8 py-3 text-lg text-white shadow-lg transition-all duration-300 hover:from-blue-700 hover:to-purple-700 hover:shadow-xl"
>
View Current Weather
<svg class="ml-2 h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 7l5 5m0 0l-5 5m5-5H6"
/>
</svg>
</Button>
</div>
</div>
{/if}
<!-- Features Grid -->
<div class="mb-20 grid gap-8 md:grid-cols-3">
{#each features as feature, index (feature.title)}
{#if mounted}
<div in:fly={{ y: 30, duration: 600, delay: 300 + index * 150 }}>
<Card
class="h-full border-0 bg-white/80 shadow-lg backdrop-blur-sm transition-all duration-300 hover:-translate-y-1 hover:shadow-xl dark:bg-gray-800/80"
>
<CardHeader>
<div
class="h-12 w-12 rounded-xl bg-gradient-to-r {feature.gradient} mb-4 flex items-center justify-center"
>
{#if feature.icon === 'calendar'}
<svg
class="h-6 w-6 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
{:else if feature.icon === 'trending-up'}
<svg
class="h-6 w-6 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"
/>
</svg>
{:else if feature.icon === 'cloud'}
<svg
class="h-6 w-6 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"
/>
</svg>
{/if}
</div>
<CardTitle class="mb-2 text-xl">{feature.title}</CardTitle>
<CardDescription class="text-gray-600 dark:text-gray-300">
{feature.description}
</CardDescription>
</CardHeader>
<CardContent class="pt-0">
<Button
href={feature.href}
variant="outline"
class="w-full hover:bg-gradient-to-r hover:{feature.gradient} transition-all duration-300 hover:border-transparent hover:text-white"
>
Explore {feature.title}
</Button>
</CardContent>
</Card>
</div>
{/if}
{/each}
</div>
<!-- Current Weather for Selected Location -->
{#if mounted && location}
<div class="mx-auto max-w-2xl" in:fade={{ duration: 600, delay: 800 }}>
<Card
class="border-blue-200 bg-gradient-to-r from-blue-500/10 to-purple-500/10 dark:border-blue-700"
>
<CardHeader>
<CardTitle class="text-center text-2xl">
Current Location: {location.name}
</CardTitle>
<CardDescription class="text-center">
{location.country}{location.latitude.toFixed(2)}°, {location.longitude.toFixed(2)}°
</CardDescription>
</CardHeader>
<CardContent class="text-center">
<Button href="/weather" variant="default" class="bg-blue-600 hover:bg-blue-700">
View Detailed Forecast
</Button>
</CardContent>
</Card>
</div>
{/if}
</div>
<!-- Features Overview Section -->
<div class="bg-white/50 py-20 dark:bg-gray-800/50">
<div class="container mx-auto px-6">
{#if mounted}
<div class="mb-16 text-center" in:fade={{ duration: 600, delay: 1000 }}>
<h2 class="mb-4 text-4xl font-bold text-gray-800 dark:text-white">
Why Choose Our Weather Service?
</h2>
<p class="mx-auto max-w-2xl text-xl text-gray-600 dark:text-gray-300">
Powered by Open-Meteo API with multiple weather models and advanced visualization
</p>
</div>
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-4">
{#each [{ title: 'Multiple Models', desc: 'Compare ECMWF, GFS, and more', icon: 'layers' }, { title: 'Extended Forecasts', desc: 'Up to 14 days ahead', icon: 'clock' }, { title: 'Interactive Charts', desc: 'Highcharts visualization', icon: 'bar-chart' }, { title: 'Real-time Data', desc: 'Always up-to-date', icon: 'refresh' }] as feature, index (feature.title)}
<div class="text-center" in:fly={{ y: 20, duration: 500, delay: 1200 + index * 100 }}>
<div
class="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-r from-blue-500 to-purple-500"
>
<svg
class="h-8 w-8 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
{#if feature.icon === 'layers'}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
/>
{:else if feature.icon === 'clock'}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
{:else if feature.icon === 'bar-chart'}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
{:else if feature.icon === 'refresh'}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
{/if}
</svg>
</div>
<h3 class="mb-2 text-lg font-semibold text-gray-800 dark:text-white">
{feature.title}
</h3>
<p class="text-gray-600 dark:text-gray-300">{feature.desc}</p>
</div>
{/each}
</div>
{/if}
</div>
</div>
</div>
<style>
:global(.container) {
max-width: 1200px;
}
</style>
+188 -79
View File
@@ -1,15 +1,11 @@
<script lang="ts">
import { get } from 'svelte/store';
import { page } from '$app/state';
import { geoLocationNameToRoute } from '$lib/utils/meteo';
import Button from '$lib/components/ui/button/button.svelte';
import { fly, fade } from 'svelte/transition';
import { onMount } from 'svelte';
import WeatherNav from '$lib/components/navigation/weather-nav.svelte';
import LocationSearch from '$lib/components/location/location-search.svelte';
import { storedLocation } from '$lib/stores/settings';
let location = get(storedLocation);
const locationRoute = geoLocationNameToRoute(location.name);
import { get } from 'svelte/store';
import { page } from '$app/stores';
interface Props {
children?: import('svelte').Snippet;
@@ -17,81 +13,194 @@
let { children }: Props = $props();
const links = [
{
title: 'Weather Forecast',
url: '/en/weather',
children: [
{
title: 'Week Prediction',
url:
'/en/weather/week/' +
(location.population
? location.population > 543000
? locationRoute
: locationRoute + '_' + location.id
: locationRoute + '_' + location.id)
},
{ title: 'Model Comparison', url: '/en/weather/compare' },
{ title: '14 Day Weather', url: '/en/weather/14-day' }
]
}
];
let location = $state(get(storedLocation));
let mounted = $state(false);
let currentWeather = $state(null);
let selectedPath = $derived.by(() => {
for (const link of links) {
if (link.children) {
for (const l of link.children) {
if (page.url.pathname.includes(l.url) || page.url.pathname.includes(l.url + '/')) {
return l;
}
}
}
if (page.url.pathname === link.url || page.url.pathname === link.url + '/') {
return link;
}
// Subscribe to location changes
storedLocation.subscribe((value) => {
location = value;
if (mounted) {
loadCurrentWeather();
}
return {};
});
let mobileNavOpened = $state(false);
onMount(() => {
mounted = true;
loadCurrentWeather();
});
const loadCurrentWeather = async () => {
if (!location?.latitude) return;
try {
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${location.latitude}&longitude=${location.longitude}&current=temperature_2m,weather_code&forecast_days=1`
);
const data = await response.json();
currentWeather = data;
} catch (error) {
console.error('Failed to load current weather:', error);
}
};
const getWeatherIcon = (code) => {
const iconMap = {
0: '☀️',
1: '🌤️',
2: '⛅',
3: '☁️',
45: '🌫️',
48: '🌫️',
51: '🌦️',
53: '🌦️',
55: '🌦️',
61: '🌧️',
63: '🌧️',
65: '🌧️',
71: '🌨️',
73: '🌨️',
75: '❄️',
95: '⛈️'
};
return iconMap[code] || '☁️';
};
const getPageTitle = () => {
const path = $page.url.pathname;
if (path.includes('/compare')) return 'Model Comparison';
if (path.includes('/14-day')) return '14 Day Forecast';
if (path.includes('/week')) return 'Week Prediction';
return 'Weather Forecast';
};
</script>
<div class="mb-12 flex flex-col md:mb-24 md:flex-row">
<aside class="w-full md:w-1/6 md:max-w-[400px] md:min-w-[230px]">
<nav class="sticky top-0 flex flex-col p-6 pb-3 md:pr-3 md:pb-6">
<Button
variant="outline"
class="flex justify-start p-3 md:hidden"
onclick={() => {
mobileNavOpened = !mobileNavOpened;
}}
>
<svg
class="lucide lucide-chevrons-up-down mr-2"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m7 15 5 5 5-5" />
<path d="m7 9 5-5 5 5" />
</svg><b>{selectedPath.title}</b>
</Button>
<WeatherNav />
<ul
class={`list-unstyled overflow-hidden duration-500 ${mobileNavOpened ? 'mt-2 max-h-[968px] md:max-h-[unset]' : 'max-h-0 md:max-h-[unset] '}`}
></ul>
</nav>
</aside>
<div
class="lg:max-w-unset flex flex-1 flex-col p-6 pt-0 md:max-w-[calc(100%-230px)] md:pt-6 md:pl-3"
>
{@render children?.()}
<div
class="min-h-screen bg-gradient-to-br from-blue-50 via-white to-cyan-50 dark:from-gray-900 dark:via-gray-800 dark:to-blue-900"
>
<!-- Hero Header Section -->
<div class="relative overflow-hidden bg-gradient-to-r from-blue-600 via-purple-600 to-cyan-600">
<div class="absolute inset-0 bg-black/10"></div>
<div class="relative">
<div class="container mx-auto px-6 py-12">
{#if mounted}
<div class="flex flex-col items-center space-y-6" in:fade={{ duration: 800 }}>
<!-- Page Title -->
<div class="text-center" in:fly={{ y: -20, duration: 600, delay: 200 }}>
<h1 class="mb-2 text-3xl font-bold text-white md:text-4xl">
{getPageTitle()}
</h1>
</div>
<!-- Location Display & Search -->
<div class="w-full max-w-2xl" in:fly={{ y: 20, duration: 600, delay: 400 }}>
<div
class="rounded-2xl bg-white/95 p-6 shadow-2xl backdrop-blur-md dark:bg-gray-800/95"
>
<!-- Current Location Display -->
{#if location}
<div
class="flex flex-col items-center justify-between space-y-4 md:flex-row md:space-y-0 md:space-x-6"
>
<!-- Location Info -->
<div class="flex flex-1 items-center space-x-4">
<div class="flex-shrink-0">
<img
class="h-12 w-12 rounded-full shadow-lg"
src="/images/country-flags/{(
location.country_code || 'united_nations'
).toLowerCase()}.svg"
alt={location.country}
/>
</div>
<div class="flex-1 text-left">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">
{location.name}
</h2>
<p class="text-gray-600 dark:text-gray-300">
{#if location.admin1}{location.admin1},
{/if}{location.country}
</p>
<p class="text-sm text-gray-500 dark:text-gray-400">
{location.latitude.toFixed(2)}°N, {location.longitude.toFixed(2)}°E
{#if location.elevation}{location.elevation.toFixed(0)}m{/if}
</p>
</div>
</div>
<!-- Current Weather -->
{#if currentWeather}
<div class="flex items-center space-x-3" in:fade={{ delay: 800 }}>
<div class="text-center">
<div class="mb-1 text-3xl">
{getWeatherIcon(currentWeather.current.weather_code)}
</div>
<div class="text-2xl font-bold text-gray-900 dark:text-white">
{Math.round(currentWeather.current.temperature_2m)}°C
</div>
</div>
</div>
{/if}
</div>
{/if}
<!-- Location Search -->
<div class="mt-6 border-t border-gray-200 pt-6 dark:border-gray-600">
<LocationSearch
label="🔍 Change location or search for a new city..."
on:location={(event) => {
storedLocation.set(event.detail);
}}
/>
</div>
</div>
</div>
<!-- Stats Row (if location has population) -->
{#if location?.population}
<div
class="flex justify-center space-x-8 text-white/90"
in:fly={{ y: 20, duration: 600, delay: 600 }}
>
<div class="text-center">
<div class="text-sm font-medium">Population</div>
<div class="text-lg font-bold">{location.population.toLocaleString()}</div>
</div>
{#if location.timezone}
<div class="text-center">
<div class="text-sm font-medium">Timezone</div>
<div class="text-lg font-bold">{location.timezone}</div>
</div>
{/if}
</div>
{/if}
</div>
{/if}
</div>
</div>
<!-- Decorative elements -->
<div class="pointer-events-none absolute top-0 left-0 h-full w-full overflow-hidden">
<div class="absolute -top-4 -right-4 h-24 w-24 rounded-full bg-white/10"></div>
<div class="absolute top-1/3 -left-8 h-16 w-16 rounded-full bg-white/5"></div>
<div class="absolute bottom-8 left-1/4 h-12 w-12 rounded-full bg-white/10"></div>
</div>
</div>
<!-- Main Content -->
<div class="container mx-auto px-6 py-8">
{#if mounted}
<div in:fade={{ duration: 600, delay: 400 }}>
{@render children?.()}
</div>
{/if}
</div>
</div>
<style>
:global(.container) {
max-width: 1200px;
}
</style>
+2 -8
View File
@@ -8,13 +8,7 @@ const location = get(storedLocation);
export const load: LayoutLoad = async () => {
return {
heroTitle: `Weather ${location.name}`,
heroDescription: location.admin1 ?? '' + ' ' + location.country,
heroImage: '/images/backgrounds/partly_cloudy.webp',
heroHeight: 400,
heroPrimaryButtonPath: null,
heroPrimaryButtonText: null,
heroSecondaryButtonPath: null,
heroSecondaryButtonText: null
title: `Weather ${location.name}`,
location: location
};
};
-703
View File
@@ -1,703 +0,0 @@
<script lang="ts">
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
import { fetchWeatherApi } from 'openmeteo';
import { type GeoLocation, storedLocation } from '$lib/stores/settings';
import { urlHashStore } from '$lib/stores/url-hash-store';
import { Label } from '$lib/components/ui/label';
import * as Select from '$lib/components/ui/select';
import LocationSearch from '$lib/components/location/location-search.svelte';
import cloudCover from './canvas/cloud-cover';
import daylight from './canvas/daylight';
import precip from './canvas/precip';
import raster from './canvas/raster';
import tempGradient from './canvas/temp-gradient';
import { defaultParameters, models } from './options';
import { getColor } from './utils/colors';
import weatherCodes from './utils/weather-codes';
const params = urlHashStore({
latitude: [$storedLocation.latitude],
longitude: [$storedLocation.longitude],
models: 'best_match',
...defaultParameters
});
let location = $state($storedLocation);
storedLocation.subscribe((value) => {
location = value;
});
let diffTemp: number | undefined = $state();
let maxTemp: number | undefined = $state();
let weatherCodesHourly: Float32Array | null | undefined = $state();
let canvasElement: HTMLCanvasElement | null | undefined = $state();
const today = new Date();
let selectedDay = $state(new Date());
let selectedDayIndex = $state(1);
let entries = $state(0);
let weather = $derived(
(async (location: GeoLocation) => {
const reqParams = {
latitude: location.latitude,
longitude: location.longitude,
elevation: location.elevation,
// timezone: location.timezone, ???
models: [$params.models],
hourly: [
'precipitation',
'precipitation_probability',
'temperature_2m',
'weather_code',
'windspeed_10m',
'winddirection_10m',
'cloud_cover',
'relative_humidity_2m'
].join(','),
forecast_days: 6,
past_days: 1,
temperature_unit: $params.temperature_unit,
wind_speed_unit: $params.wind_speed_unit,
precipitation_unit: $params.precipitation_unit
};
const url = 'https://api.open-meteo.com/v1/forecast';
const responses = await fetchWeatherApi(url, reqParams);
const response = responses[0];
const utcOffsetSeconds = response.utcOffsetSeconds();
const daily = response.daily()!;
const hourly = response.hourly()!;
weatherCodesHourly = hourly.variables(3)?.valuesArray();
let hourlyTime = [
...Array((Number(hourly.timeEnd()) - Number(hourly.time())) / hourly.interval())
].map(
(_, i) =>
new Date((Number(hourly.time()) + i * hourly.interval() + utcOffsetSeconds) * 1000)
);
const hourlyTemps = hourly.variables(2)?.valuesArray();
const hourlyCloudCover = hourly.variables(6)?.valuesArray();
const hourlyPrecip = hourly.variables(0)?.valuesArray();
const indexes = [];
for (const [index, _] of hourlyTemps.entries()) {
indexes.push(index);
}
const maxX = 10000;
const maxY = 500;
const deltaX = 10000 / hourly.variables(0)?.valuesArray()?.length;
const ctx = canvasElement?.getContext('2d');
if (ctx) {
ctx.clearRect(0, 0, maxX, maxY);
const minTemp = Math.min(
...hourly
.variables(2)
?.valuesArray()
.filter((t) => !isNaN(t))
);
maxTemp = Math.max(
...hourly
.variables(2)
?.valuesArray()
.filter((t) => !isNaN(t))
);
diffTemp = maxTemp - minTemp;
const config: ConfigInterface = {
maxX: maxX,
maxY: maxY,
deltaX: deltaX,
minTemp: minTemp,
maxTemp: maxTemp,
diffTemp: diffTemp
};
// create canvas
daylight(ctx, config, hourlyTime);
raster(ctx, config, hourlyTime, today, canvasElement);
tempGradient(ctx, config, hourlyTemps, $params.temperature_unit);
cloudCover(ctx, config, hourlyCloudCover, canvasElement);
precip(ctx, config, hourlyPrecip, canvasElement);
}
return {
entries: [
{
id: 0,
name: 'temperature_2m',
title: 'Temperature',
values: hourly
.variables(2)
?.valuesArray()
?.map((t) => t.toFixed(1))
},
{
id: 1,
name: 'precipitation',
title: 'Precipitation',
values: hourly
.variables(0)
?.valuesArray()
?.map((p) => p.toFixed(1))
},
{
id: 2,
name: 'precipitation_probability',
title: 'Precip Prob.',
values: hourly
.variables(1)
?.valuesArray()
?.map((p) => p.toFixed(0))
},
{
id: 3,
name: 'windspeed_10m',
title: 'Wind',
values: hourly
.variables(4)
?.valuesArray()
?.map((p) => p.toFixed(0))
},
{
id: 4,
name: 'relative_humidity_2m',
title: 'Rel. Hum.',
values: hourly
.variables(7)
?.valuesArray()
?.map((p) => p.toFixed(0))
}
],
entriesLength: hourly.variables(0)?.valuesArray()?.length,
hourlyTime: hourlyTime,
windDirections: hourly.variables(5)?.valuesArray(),
indexes: indexes
};
})(location)
);
let weatherDaily = $derived(
(async (location: GeoLocation) => {
const reqParams = {
latitude: location.latitude,
longitude: location.longitude,
elevation: location.elevation,
// timezone: location.timezone, ???
models: [$params.models],
daily: [
'weather_code',
'temperature_2m_max',
'temperature_2m_min',
'sunrise',
'sunset',
'sunshine_duration',
'precipitation_sum',
'windspeed_10m_max',
'windgusts_10m_max',
'winddirection_10m_dominant'
].join(','),
forecast_days: 6,
past_days: 1,
temperature_unit: $params.temperature_unit,
wind_speed_unit: $params.wind_speed_unit,
precipitation_unit: $params.precipitation_unit
};
const url = 'https://api.open-meteo.com/v1/forecast';
const responses = await fetchWeatherApi(url, reqParams);
const response = responses[0];
const utcOffsetSeconds = response.utcOffsetSeconds();
const daily = response.daily()!;
return {
daily: {
time: [...Array((Number(daily.timeEnd()) - Number(daily.time())) / daily.interval())].map(
(_, i) =>
new Date((Number(daily.time()) + i * daily.interval() + utcOffsetSeconds) * 1000)
),
weather_code: daily.variables(0)!,
temperature_2m_max: daily.variables(1)!,
temperature_2m_min: daily.variables(2)!,
sunrise: daily.variables(3)!,
sunset: daily.variables(4)!,
sunshine_duration: daily.variables(5)!,
precipitation_sum: daily.variables(6)!,
windspeed_10m_max: daily.variables(7)!,
windgusts_10m_max: daily.variables(8)!,
winddirection_10m_dominant: daily.variables(9)!
}
};
})(location)
);
let winddir = true;
entries = 6;
let scrollDiv: HTMLElement = $state();
let tableCells;
const switchDay = (date: Date, index: number) => {
selectedDay = date;
tableCells = document.querySelectorAll('td.time');
for (let tableCell of tableCells) {
if (Number(tableCell.dataset['date']) === selectedDay.getDate()) {
scrollDiv.scrollTo({ left: tableCell.offsetLeft - 110, behavior: 'smooth' });
break;
}
}
selectedDayIndex = index;
};
onMount(() => {
setTimeout(() => {
tableCells = document.querySelectorAll('td.time');
for (let tableCell of tableCells) {
if (Number(tableCell.dataset['date']) === selectedDay.getDate()) {
scrollDiv.scrollTo({ left: tableCell.offsetLeft - 110 });
break;
}
}
}, 150);
document.onkeydown = (e) => {
if (!scrollDiv === document.activeElement || !scrollDiv.contains(document.activeElement)) {
if (e.key === 'ArrowLeft') {
if (selectedDay.getDate() >= today.getDate()) {
let newDate = new Date();
newDate.setDate(selectedDay.getDate() - 1);
switchDay(newDate);
}
}
if (e.key === 'ArrowRight') {
if (selectedDay.getDate() <= today.getDate() + 4) {
let newDate = new Date();
newDate.setDate(selectedDay.getDate() + 1);
switchDay(newDate);
}
}
}
};
});
let modelSelected = $derived(models.find((mo) => String(mo.value) === $params.models));
// let modelSelectedValue = $derived($params.models[0]);
//
</script>
<svelte:head>
<title>Weather | Open-Meteo.com</title>
<link rel="canonical" href="https://open-meteo.com/en/weather" />
<meta name="description" content="segseg" />
</svelte:head>
<div class="">
<div class="weather-content" style="min-height: 50vh">
<div
in:fade
out:fade
style="min-height: 256px"
class="weather-week gap-md-2 mb-4 flex flex-col md:flex-row"
>
{#await weatherDaily then wd}
{#each wd.daily.time as time, index (index)}
{@const selected = time.getDate() === selectedDay.getDate()}
{#if !isNaN(wd.daily.temperature_2m_max.values(index).toFixed(1))}
<button
style="transition: 300ms; min-width: 13%; {selected ? 'transform: scale(1.025)' : ''}"
class="cursor-pointer"
onclick={() => {
switchDay(time, index);
}}
>
<div
class="gap-md-1 flex flex-row items-center justify-center rounded-xl p-1 md:flex-col md:justify-center md:p-3 {selected
? 'bg-accent'
: ''}"
>
<div class="weather-week-date">
<b>{time.getDate()} - {time.getMonth() + 1}</b>
</div>
<div
data-text={time.toLocaleDateString('en-GB', { weekday: 'long' })}
class="grow-text relative mx-auto inline-flex flex-col {selected
? 'font-bold'
: ''}"
>
{time.toLocaleDateString('en-GB', { weekday: 'long' })}
</div>
<div class="weather-week-icon pe-none py-2">
<svg class="fill-foreground" width="60px" height="60px">
<use
xlink:href="/images/weather-icons/wi-day-{weatherCodes[
wd.daily.weather_code.values(index)
]}.svg#Layer_1"
></use>
</svg>
</div>
<div
class="weather-temp-max flex min-w-[65px] justify-center rounded-t p-1 text-sm"
style={`background-color: ${getColor(wd.daily.temperature_2m_max.values(index).toFixed(0), $params.temperature_unit)}; color: ${wd.daily.temperature_2m_min.values(index) < ($params.temperature_unit === 'celsius' ? 4 : 7) || wd.daily.temperature_2m_min.values(index) >= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
>
{wd.daily.temperature_2m_max.values(index).toFixed(1)}
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}
</div>
<div
class="weather-temp-min flex min-w-[65px] justify-center rounded-b p-1 text-sm"
style={`background: ${getColor(wd.daily.temperature_2m_min.values(index).toFixed(0), $params.temperature_unit)}; color: ${wd.daily.temperature_2m_min.values(index) < ($params.temperature_unit === 'celsius' ? 4 : 7) || wd.daily.temperature_2m_min.values(index) >= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
>
{wd.daily.temperature_2m_min.values(index).toFixed(1)}
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}
</div>
<div class="mt-2 flex items-center justify-center gap-1">
<div class="relative flex h-6 w-6 items-center justify-center">
<div class="absolute">
<svg class="fill-foreground" width="26px" height="26px">
<use
class="stroke-2"
xlink:href="/images/weather-icons/wi-day-sunny.svg#Layer_1"
></use>
</svg>
</div>
</div>
{Number(wd.daily.sunshine_duration.values(index) / 3600).toFixed(0)}h
</div>
<div class="mt-1 flex items-center justify-center">
<div class="relative flex h-6 w-6 items-center justify-center">
<div class="absolute">
<svg class="fill-foreground" width="28px" height="28px">
<use
class="stroke-2"
xlink:href="/images/weather-icons/wi-raindrop.svg#Layer_1"
></use>
</svg>
</div>
</div>
{Number(wd.daily.precipitation_sum.values(index)).toFixed(
1
)}{$params.precipitation_unit === 'mm' ? 'mm' : "'"}
</div>
</div>
</button>
{/if}
{/each}
{:catch error}
<p style="color: red">{error.message}</p>
{/await}
</div>
<div class="ml-22 md:ml-0">
<h3 class="text-xl font-bold">
{selectedDay.toLocaleDateString('en-GB', { weekday: 'long' })}
<small>
{selectedDay.getDate() === new Date(today.getTime() - 24 * 60 * 60 * 1000).getDate()
? ' (Yesterday)'
: ''}
{selectedDay.getDate() === today.getDate() ? ' (Today)' : ''}
{selectedDay.getDate() === new Date(today.getTime() + 24 * 60 * 60 * 1000).getDate()
? ' (Tomorrow)'
: ''}
</small>
</h3>
</div>
<div
bind:this={scrollDiv}
style=" height: {218 + entries * 27.5}px; "
class="w-ful relative -mx-5 overflow-x-scroll overflow-y-hidden md:-ml-[110px]"
>
<canvas
bind:this={canvasElement}
id="weather_week_canvas"
class="border border-border"
style="margin-top: 24px; margin-left: 110px; width: 5000px; height: 200px; "
height="500px"
width="10000px"
></canvas>
<table in:fade class="absolute bottom-0 border-b border-border">
<caption style="display:none"> Weather Week {location.name} </caption>
<tbody>
{#await weather then weather}
<tr>
<th
scope="row"
class="time"
style="color: transparent; padding-left: 4px; background: transparent; left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>Time</th
>
{#each weather.indexes as index, j (j)}
<td
class="time {weather.hourlyTime[index].getDate() === today.getDate() &&
weather.hourlyTime[index].getHours() === today.getHours()
? 'now'
: ''}"
data-date={weather.hourlyTime[index].getDate()}
data-time={weather.hourlyTime[index].getHours() + ':00'}
style="font-size: 11px; position: absolute; bottom: {188 +
27 * entries}px; left:{111 +
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
>{weather.hourlyTime[index].getHours() < 10 ? '0' : ''}{weather.hourlyTime[
index
].getHours()}</td
>
{/each}
</tr>
<!-- icons -->
<tr>
<th
scope="row"
style="color: transparent; padding-left: 4px; background: transparent; left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>Icons</th
>
{#each weather.indexes as index, j (j)}
{@const now =
weather.hourlyTime[index].getDate() === today.getDate() &&
weather.hourlyTime[index].getHours() === today.getHours()}
<td
style="position: absolute; bottom: {27.5 * entries -
24 +
0.8 * 200 -
0.54 *
200 *
((maxTemp - weather.entries[0].values[index]) / diffTemp)}px; left:{116 +
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
><svg class="fill-foreground {now ? 'scale-125' : ''}" width="20px" height="20px">
<use
class="stroke-2"
xlink:href="/images/weather-icons/wi-{weather.hourlyTime[index].getHours() >
6 && weather.hourlyTime[index].getHours() < 21
? 'day'
: 'night'}-{weatherCodes[weatherCodesHourly[index]]}.svg#Layer_1"
></use>
</svg></td
>
{/each}
</tr>
<!-- min / max -->
<tr>
<th
scope="row"
style="color: transparent; padding-left: 4px; background: transparent; left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>Temp graph</th
>
{#each weather.indexes as index, j (j)}
{@const temp = weather.entries[0].values[index]}
{#if !isNaN(temp)}
<td
class={weather.hourlyTime[index].getDate() === today.getDate() &&
weather.hourlyTime[index].getHours() === today.getHours()
? 'now'
: ''}
style="position: absolute; bottom: {27.5 * entries -
49 +
0.8 * 200 -
0.55 * 200 * ((maxTemp - temp) / diffTemp)}px; left:{111 +
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
>{temp.toFixed(0)}</td
>
{/if}
{/each}
</tr>
{#each weather.entries as entry, i (i)}
<tr class="border-t border-border">
<th
scope="row"
class="bg-background text-left"
style="left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>{entry.title}</th
>
{#each weather.indexes as index, j (j)}
{#if !isNaN(entry.values[index])}
<td
class="border-r border-border {weather.hourlyTime[index].getDate() ===
today.getDate() && weather.hourlyTime[index].getHours() === today.getHours()
? 'now'
: ''}"
style="min-width: {5000 / weather.entriesLength}px; max-width: {5000 /
weather.entriesLength}px;
{entry.name === 'temperature_2m'
? 'background: ' +
getColor(
weather.entries[0].values[index].toFixed(0),
$params.temperature_unit
)
: ''};
{entry.name === 'temperature_2m'
? 'color: ' +
(weather.entries[0].values[index] <
($params.temperature_unit === 'celsius' ? -13 : 7) ||
weather.entries[0].values[index] >=
($params.temperature_unit === 'celsius' ? 40 : 104)
? 'white'
: 'black')
: ''};
{entry.name === 'precipitation_probability'
? 'background: rgba(0, 0, 230,' +
weather.entries[2].values[index] / 120 +
')'
: ''};
{entry.name === 'precipitation_probability'
? 'color: ' +
(weather.entries[2].values[index] > 50
? 'white'
: 'hsl(var(--foreground)')
: ''};
{entry.name === 'relative_humidity_2m'
? 'background: rgba(0, 240, 240,' +
weather.entries[4].values[index] ** 3.8 / 10 ** 8.2 +
')'
: ''};"
>{entry.name === 'precipitation' || entry.name === 'temperature_2m'
? entry.values[index].toFixed(1)
: entry.values[index]}</td
>
{/if}
{/each}
</tr>
{/each}
{#if winddir}
<!-- winddir -->
<tr class="border-t border-border">
<th
scope="row"
class="bg-background text-left"
style="z-index: 20; left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>Wind Dir.</th
>
{#each weather.indexes as index, j (j)}
{#if !isNaN(weather.windDirections[index])}
<td
class="border-r border-border {weather.hourlyTime[index].getDate() ===
today.getDate() && weather.hourlyTime[index].getHours() === today.getHours()
? 'now'
: ''}"
style="transform: rotate({weather.windDirections[
index
]}deg);min-width: {5000 / weather.entriesLength}px; max-width: {5000 /
weather.entriesLength}px;"
><svg class="fill-foreground" width="25px" height="25px">
<use xlink:href="/images/weather-icons/wi-direction-down.svg#Layer_1"></use>
</svg></td
>
{/if}
{/each}
</tr>
{/if}
{/await}
</tbody>
</table>
</div>
</div>
{#await weatherDaily then wd}
{@const sunrise = new Date(Number(wd.daily.sunrise.valuesInt64(selectedDayIndex)) * 1000)}
{@const sunset = new Date(Number(wd.daily.sunset.valuesInt64(selectedDayIndex)) * 1000)}
<div class="mt-6">
<div class="flex items-center gap-1">
<svg class="fill-foreground" width="28px" height="28px">
<use class="stroke-2" xlink:href="/images/weather-icons/wi-sunrise.svg#Layer_1"></use>
</svg>Sunrise: {pad(sunrise.getHours())}:{pad(sunrise.getMinutes())}
</div>
<div class="flex items-center gap-1">
<svg class="fill-foreground" width="28px" height="28px">
<use class="stroke-2" xlink:href="/images/weather-icons/wi-sunset.svg#Layer_1"></use>
</svg>
Sunset: {pad(sunset.getHours())}:{pad(sunset.getMinutes())}
</div>
</div>
{/await}
<div>
<div class="mt-6 flex gap-6 md:mt-12">
<div class="relative w-1/2">
<Select.Root name="model_selection" type="single" bind:value={$params.models}>
<Select.Trigger
aria-label="Forecast days input"
class="h-12 cursor-pointer pt-6 [&_svg]:mb-3">{modelSelected?.label}</Select.Trigger
>
<Select.Content preventScroll={false} class="border-border">
{#each models as mo}
<Select.Item class="cursor-pointer" value={mo.value}>{mo.label}</Select.Item>
{/each}
</Select.Content>
<Label class="absolute top-[0.35rem] left-2 z-10 px-1 text-xs text-muted-foreground"
>Weather model</Label
>
</Select.Root>
</div>
<div class="relative w-1/2">
<LocationSearch
style="height: 40px"
on:location={(event) => storedLocation.set(event.detail)}
label="Search Location"
/>
</div>
</div>
</div>
<div class="mt-6 mb-6">
<h2 class="text-2xl md:text-3xl">Color scale example</h2>
<div class="mt-3 grid grid-cols-4 md:mt-6">
{#if $params.temperature_unit == 'celsius'}
{#each [...Array(101).keys()].map((i) => -40 + i) as temp}
<div
class="weather-temp-max flex min-w-[70px] justify-center rounded p-1"
style={`background-color: ${getColor(temp, $params.temperature_unit)}; color: ${temp <= ($params.temperature_unit === 'celsius' ? 4 : 7) || temp >= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
>
{temp} °C <br />
{getColor(temp, $params.temperature_unit)}
</div>
{/each}
{:else}
{#each [...Array(91).keys()].map((i) => -40 + i * 2) as temp}
<div
class="weather-temp-max flex min-w-[70px] justify-center rounded p-1"
style={`background-color: ${getColor(temp, $params.temperature_unit)}; color: ${temp <= ($params.temperature_unit === 'celsius' ? 4 : 7) || temp >= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
>
{temp}
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}<br />
{getColor(temp, $params.temperature_unit)}
</div>
{/each}
{/if}
</div>
</div>
</div>
<style>
.now {
font-weight: bold;
}
td {
text-align: center;
font-size: 13px;
}
.weather-week-icon {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
background: #0061a5;
margin: 5px 0;
border-radius: 5px;
}
</style>
+8
View File
@@ -0,0 +1,8 @@
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from '$types';
export const prerender = true;
export const load = (async () => {
throw redirect(303, '/weather/week/');
}) satisfies PageLoad;
-12
View File
@@ -11,8 +11,6 @@
import { Label } from '$lib/components/ui/label';
import { Switch } from '$lib/components/ui/switch';
import LocationSearch from '$lib/components/location/location-search.svelte';
import '../compare/highcharts.css';
import { defaultParameters } from './options';
@@ -295,16 +293,6 @@
<div class="">
<div class="mt-6 flex flex-col items-center gap-6 md:mt-12 md:flex-row">
<div class="relative w-1/4">
<LocationSearch
style="height: 40px"
on:location={(event) => {
storedLocation.set(event.detail);
window.location.reload();
}}
label="Search Location"
/>
</div>
<div class="flex gap-2">
<Switch
id="show_legend"
+2 -2
View File
@@ -2,7 +2,7 @@ export default (
ctx: CanvasRenderingContext2D | null | undefined,
config: ConfigInterface,
series: Float32Array | null | undefined,
canvasElement: HTMLCanvasElement | null
canvasElement: HTMLCanvasElement
): void => {
if (ctx && series) {
ctx.beginPath();
@@ -41,7 +41,7 @@ export default (
);
// same series but reversed
for (const [ind, v] of series.entries()) {
for (const [ind, _v] of series.entries()) {
const index = series.length - 1 - ind;
const value = series[index];
const nextValue = series[index - 1];
+1 -1
View File
@@ -2,7 +2,7 @@ export default (
ctx: CanvasRenderingContext2D | null | undefined,
config: ConfigInterface,
series: Float32Array | null | undefined,
canvasElement: HTMLCanvasElement | null
canvasElement: HTMLCanvasElement
): void => {
if (ctx && series) {
for (const [index, value] of series.entries()) {
+3 -3
View File
@@ -3,10 +3,10 @@ export default (
config: ConfigInterface,
series: Date[],
today: Date,
canvasElement: HTMLCanvasElement | null
canvasElement: HTMLCanvasElement
): void => {
if (ctx && series) {
for (const [index, _] of series.entries()) {
for (const [index, _v] of series.entries()) {
ctx.beginPath();
ctx.moveTo(index * config.deltaX, 0);
ctx.lineTo(index * config.deltaX, config.maxY);
@@ -24,7 +24,7 @@ export default (
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.lineWidth = 5;
let minutes = today.getMinutes();
const minutes = today.getMinutes();
ctx.moveTo(index * config.deltaX + (config.deltaX / 60) * minutes, 0);
ctx.lineTo(index * config.deltaX + (config.deltaX / 60) * minutes, config.maxY);
ctx.stroke();
+1 -1
View File
@@ -18,7 +18,7 @@ export default (
0,
0.25 * config.maxY + ((config.maxTemp - series[0]) / config.diffTemp) * 0.55 * config.maxY
);
for (const [index, value] of series?.filter((t) => !isNaN(t)).entries()) {
for (const [index, value] of series.filter((t) => !isNaN(t)).entries()) {
const indexDiffTemp = config.maxTemp - value;
const indexDiffTempNext = config.maxTemp - series[index + 1];
+1 -15
View File
@@ -12,8 +12,6 @@
import { Label } from '$lib/components/ui/label';
import { Switch } from '$lib/components/ui/switch';
import LocationSearch from '$lib/components/location/location-search.svelte';
import { hourly, models } from '../options';
import './highcharts.css';
import { defaultParameters } from './options';
@@ -102,14 +100,13 @@
let average = new Array(data.hourly.time.length).fill(0);
let averageCount = new Array(data.hourly.time.length).fill(0);
let variableCount = 0;
for (let [model, values] of Object.entries(data.hourly)) {
if (model === 'time') {
continue;
}
if (model.startsWith(variable)) {
for (let [index, val] of values.entries()) {
if (!!val) {
if (val) {
let avVal = average[index];
average[index] = avVal + val;
averageCount[index]++;
@@ -133,7 +130,6 @@
pointInterval: pointInterval
});
}
variableCount++;
}
}
@@ -295,16 +291,6 @@
<div class="">
<div class="mt-6 flex flex-col items-center gap-6 md:mt-12 md:flex-row">
<div class="relative w-1/4">
<LocationSearch
style="height: 40px"
on:location={(event) => {
storedLocation.set(event.detail);
window.location.reload();
}}
label="Search Location"
/>
</div>
<div class="flex gap-2">
<Switch
id="show_legend"
+5 -5
View File
@@ -13,9 +13,9 @@ export function rgbToHex(rgb: string) {
b,
hex = '';
if ((result = rgbRegex.exec(rgb))) {
r = componentFromStr(result[1], result[2]);
g = componentFromStr(result[3], result[4]);
b = componentFromStr(result[5], result[6]);
r = componentFromStr(result[1], Number(result[2]));
g = componentFromStr(result[3], Number(result[4]));
b = componentFromStr(result[5], Number(result[6]));
hex = (0x1000000 + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
@@ -25,9 +25,9 @@ export function rgbToHex(rgb: string) {
return hex;
}
export const getColor = (temp: number, unit = 'celsius'): string => {
export const getColor = (tempString: string, unit = 'celsius'): string => {
let index = 0;
temp = Number(temp);
const temp = Number(tempString);
if (unit === 'celsius') {
if (temp <= -40) {
index = 0;
-14
View File
@@ -1,14 +0,0 @@
import { get } from 'svelte/store';
import { storedLocation } from '$lib/stores/settings';
import type { LayoutLoad } from './$types';
const location = get(storedLocation);
export const load: LayoutLoad = async () => {
return {
heroTitle: `Weather Week ${location.name}`,
heroDescription: location.admin1 ?? '' + ' ' + location.country
};
};
+2 -6
View File
@@ -1,21 +1,17 @@
import { get } from 'svelte/store';
import { redirect } from '@sveltejs/kit';
import { storedLocation } from '$lib/stores/settings';
import { geoLocationNameToRoute } from '$lib/utils/meteo';
import type { PageLoad } from '$types';
export const prerender = true;
export const load = (async (event) => {
export const load = (async () => {
const location = get(storedLocation);
const locationRoute = geoLocationNameToRoute(location.name);
throw redirect(
303,
'/en/weather/week/' +
'/weather/week/' +
(location.population
? location.population > 543000
? locationRoute
+657 -5
View File
@@ -1,9 +1,661 @@
<script lang="ts">
let { data } = $props();
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
const location = data.location;
import { fetchWeatherApi } from 'openmeteo';
import { type GeoLocation, storedLocation } from '$lib/stores/settings';
import { urlHashStore } from '$lib/stores/url-hash-store';
import { Label } from '$lib/components/ui/label';
import * as Select from '$lib/components/ui/select';
import cloudCover from '../../canvas/cloud-cover';
import daylight from '../../canvas/daylight';
import precip from '../../canvas/precip';
import raster from '../../canvas/raster';
import tempGradient from '../../canvas/temp-gradient';
import { defaultParameters, models } from '../../options';
import { getColor } from '../../utils/colors';
import weatherCodes from '../../utils/weather-codes';
import { SvelteDate } from 'svelte/reactivity';
import { pad } from '$lib/utils';
const params = urlHashStore({
latitude: [$storedLocation.latitude],
longitude: [$storedLocation.longitude],
models: 'best_match',
...defaultParameters
});
let location = $state($storedLocation);
storedLocation.subscribe((value) => {
location = value;
});
let diffTemp: number | undefined = $state();
let maxTemp: number | undefined = $state();
let weatherCodesHourly: Float32Array | null | undefined = $state();
let canvasElement: HTMLCanvasElement | null | undefined = $state();
const today = new Date();
let selectedDay = $state(new Date());
let selectedDayIndex = $state(1);
let entries = $state(0);
let weather = $derived(
(async (location: GeoLocation) => {
const reqParams = {
latitude: location.latitude,
longitude: location.longitude,
elevation: location.elevation,
// timezone: location.timezone, ???
models: [$params.models],
hourly: [
'precipitation',
'precipitation_probability',
'temperature_2m',
'weather_code',
'windspeed_10m',
'winddirection_10m',
'cloud_cover',
'relative_humidity_2m'
].join(','),
forecast_days: 6,
past_days: 1,
temperature_unit: $params.temperature_unit,
wind_speed_unit: $params.wind_speed_unit,
precipitation_unit: $params.precipitation_unit
};
const url = 'https://api.open-meteo.com/v1/forecast';
const responses = await fetchWeatherApi(url, reqParams);
const response = responses[0];
const utcOffsetSeconds = response.utcOffsetSeconds();
const hourly = response.hourly()!;
weatherCodesHourly = hourly.variables(3)?.valuesArray();
let hourlyTime = [
...Array((Number(hourly.timeEnd()) - Number(hourly.time())) / hourly.interval())
].map(
(_, i) =>
new Date((Number(hourly.time()) + i * hourly.interval() + utcOffsetSeconds) * 1000)
);
const hourlyTemps = hourly.variables(2)?.valuesArray();
const hourlyCloudCover = hourly.variables(6)?.valuesArray();
const hourlyPrecip = hourly.variables(0)?.valuesArray();
const indexes = [];
if (hourlyTemps) {
for (const index of hourlyTemps.keys()) {
indexes.push(index);
}
}
const maxX = 10000;
const maxY = 500;
const deltaX = 10000 / hourly.variables(0)?.valuesArray()?.length;
const ctx = canvasElement?.getContext('2d');
if (ctx) {
ctx.clearRect(0, 0, maxX, maxY);
const minTemp = Math.min(
...(hourly.variables(2)?.valuesArray() ?? []).filter((t) => !isNaN(t))
);
maxTemp = Math.max(...(hourly.variables(2)?.valuesArray() ?? []).filter((t) => !isNaN(t)));
diffTemp = maxTemp - minTemp;
const config: ConfigInterface = {
maxX: maxX,
maxY: maxY,
deltaX: deltaX,
minTemp: minTemp,
maxTemp: maxTemp,
diffTemp: diffTemp
};
// create canvas
daylight(ctx, config, hourlyTime);
raster(ctx, config, hourlyTime, today, canvasElement);
tempGradient(ctx, config, hourlyTemps, $params.temperature_unit);
cloudCover(ctx, config, hourlyCloudCover, canvasElement);
precip(ctx, config, hourlyPrecip, canvasElement);
}
return {
entries: [
{
id: 0,
name: 'temperature_2m',
title: 'Temperature',
values: hourly
.variables(2)
?.valuesArray()
?.map((t) => t.toFixed(1))
},
{
id: 1,
name: 'precipitation',
title: 'Precipitation',
values: hourly
.variables(0)
?.valuesArray()
?.map((p) => p.toFixed(1))
},
{
id: 2,
name: 'precipitation_probability',
title: 'Precip Prob.',
values: hourly
.variables(1)
?.valuesArray()
?.map((p) => p.toFixed(0))
},
{
id: 3,
name: 'windspeed_10m',
title: 'Wind',
values: hourly
.variables(4)
?.valuesArray()
?.map((p) => p.toFixed(0))
},
{
id: 4,
name: 'relative_humidity_2m',
title: 'Rel. Hum.',
values: hourly
.variables(7)
?.valuesArray()
?.map((p) => p.toFixed(0))
}
],
entriesLength: hourly.variables(0)?.valuesArray()?.length,
hourlyTime: hourlyTime,
windDirections: hourly.variables(5)?.valuesArray(),
indexes: indexes
};
})(location)
);
let weatherDaily = $derived(
(async (location: GeoLocation) => {
const reqParams = {
latitude: location.latitude,
longitude: location.longitude,
elevation: location.elevation,
// timezone: location.timezone, ???
models: [$params.models],
daily: [
'weather_code',
'temperature_2m_max',
'temperature_2m_min',
'sunrise',
'sunset',
'sunshine_duration',
'precipitation_sum',
'windspeed_10m_max',
'windgusts_10m_max',
'winddirection_10m_dominant'
].join(','),
forecast_days: 6,
past_days: 1,
temperature_unit: $params.temperature_unit,
wind_speed_unit: $params.wind_speed_unit,
precipitation_unit: $params.precipitation_unit
};
const url = 'https://api.open-meteo.com/v1/forecast';
const responses = await fetchWeatherApi(url, reqParams);
const response = responses[0];
const utcOffsetSeconds = response.utcOffsetSeconds();
const daily = response.daily()!;
return {
daily: {
time: [...Array((Number(daily.timeEnd()) - Number(daily.time())) / daily.interval())].map(
(_, i) =>
new Date((Number(daily.time()) + i * daily.interval() + utcOffsetSeconds) * 1000)
),
weather_code: daily.variables(0)!,
temperature_2m_max: daily.variables(1)!,
temperature_2m_min: daily.variables(2)!,
sunrise: daily.variables(3)!,
sunset: daily.variables(4)!,
sunshine_duration: daily.variables(5)!,
precipitation_sum: daily.variables(6)!,
windspeed_10m_max: daily.variables(7)!,
windgusts_10m_max: daily.variables(8)!,
winddirection_10m_dominant: daily.variables(9)!
}
};
})(location)
);
let winddir = true;
entries = 6;
let scrollDiv: HTMLElement = $state();
let tableCells;
const switchDay = (date: SvelteDate, index: number) => {
selectedDay = date;
tableCells = document.querySelectorAll('td.time');
for (let tableCell of tableCells) {
if (Number(tableCell.dataset['date']) === selectedDay.getDate()) {
scrollDiv.scrollTo({ left: tableCell.offsetLeft - 110, behavior: 'smooth' });
break;
}
}
selectedDayIndex = index;
};
onMount(() => {
setTimeout(() => {
tableCells = document.querySelectorAll('td.time');
for (let tableCell of tableCells) {
if (Number(tableCell.dataset['date']) === selectedDay.getDate()) {
scrollDiv.scrollTo({ left: tableCell.offsetLeft - 110 });
break;
}
}
}, 150);
document.onkeydown = (e) => {
if (!scrollDiv === document.activeElement || !scrollDiv.contains(document.activeElement)) {
if (e.key === 'ArrowLeft') {
if (selectedDay.getDate() >= today.getDate()) {
let newDate = new SvelteDate();
newDate.setDate(selectedDay.getDate() - 1);
switchDay(newDate);
}
}
if (e.key === 'ArrowRight') {
if (selectedDay.getDate() <= today.getDate() + 4) {
let newDate = new SvelteDate();
newDate.setDate(selectedDay.getDate() + 1);
switchDay(newDate);
}
}
}
};
});
let modelSelected = $derived(models.find((mo) => String(mo.value) === $params.models));
// let modelSelectedValue = $derived($params.models[0]);
//
</script>
<h1>
{location ? location.name : ''}, population: {location.population}
</h1>
<svelte:head>
<title>Weather | Open-Meteo.com</title>
<link rel="canonical" href="https://open-meteo.com/weather" />
<meta name="description" content="segseg" />
</svelte:head>
<div class="">
<div class="weather-content" style="min-height: 50vh">
<div
in:fade
out:fade
style="min-height: 256px"
class="weather-week gap-md-2 mb-4 flex flex-col md:flex-row"
>
{#await weatherDaily then wd}
{#each wd.daily.time as time, index (index)}
{@const selected = time.getDate() === selectedDay.getDate()}
{#if !isNaN(wd.daily.temperature_2m_max.values(index).toFixed(1))}
<button
style="transition: 300ms; min-width: 13%; {selected ? 'transform: scale(1.025)' : ''}"
class="cursor-pointer"
onclick={() => {
switchDay(time, index);
}}
>
<div
class="gap-md-1 flex flex-row items-center justify-center rounded-xl p-1 md:flex-col md:justify-center md:p-3 {selected
? 'bg-accent'
: ''}"
>
<div class="weather-week-date">
<b>{time.getDate()} - {time.getMonth() + 1}</b>
</div>
<div
data-text={time.toLocaleDateString('en-GB', { weekday: 'long' })}
class="grow-text relative mx-auto inline-flex flex-col {selected
? 'font-bold'
: ''}"
>
{time.toLocaleDateString('en-GB', { weekday: 'long' })}
</div>
<div class="weather-week-icon pe-none py-2">
<svg class="fill-foreground" width="60px" height="60px">
<use
xlink:href="/images/weather-icons/wi-day-{weatherCodes[
wd.daily.weather_code.values(index)
]}.svg#Layer_1"
></use>
</svg>
</div>
<div
class="weather-temp-max flex min-w-[65px] justify-center rounded-t p-1 text-sm"
style={`background-color: ${getColor(wd.daily.temperature_2m_max.values(index).toFixed(0), $params.temperature_unit)}; color: ${wd.daily.temperature_2m_min.values(index) < ($params.temperature_unit === 'celsius' ? 4 : 7) || wd.daily.temperature_2m_min.values(index) >= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
>
{wd.daily.temperature_2m_max.values(index).toFixed(1)}
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}
</div>
<div
class="weather-temp-min flex min-w-[65px] justify-center rounded-b p-1 text-sm"
style={`background: ${getColor(wd.daily.temperature_2m_min.values(index).toFixed(0), $params.temperature_unit)}; color: ${wd.daily.temperature_2m_min.values(index) < ($params.temperature_unit === 'celsius' ? 4 : 7) || wd.daily.temperature_2m_min.values(index) >= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
>
{wd.daily.temperature_2m_min.values(index).toFixed(1)}
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}
</div>
<div class="mt-2 flex items-center justify-center gap-1">
<div class="relative flex h-6 w-6 items-center justify-center">
<div class="absolute">
<svg class="fill-foreground" width="26px" height="26px">
<use
class="stroke-2"
xlink:href="/images/weather-icons/wi-day-sunny.svg#Layer_1"
></use>
</svg>
</div>
</div>
{Number(wd.daily.sunshine_duration.values(index) / 3600).toFixed(0)}h
</div>
<div class="mt-1 flex items-center justify-center">
<div class="relative flex h-6 w-6 items-center justify-center">
<div class="absolute">
<svg class="fill-foreground" width="28px" height="28px">
<use
class="stroke-2"
xlink:href="/images/weather-icons/wi-raindrop.svg#Layer_1"
></use>
</svg>
</div>
</div>
{Number(wd.daily.precipitation_sum.values(index)).toFixed(
1
)}{$params.precipitation_unit === 'mm' ? 'mm' : "'"}
</div>
</div>
</button>
{/if}
{/each}
{:catch error}
<p style="color: red">{error.message}</p>
{/await}
</div>
<div class="ml-22 md:ml-0">
<h3 class="text-xl font-bold">
{selectedDay.toLocaleDateString('en-GB', { weekday: 'long' })}
<small>
{selectedDay.getDate() === new Date(today.getTime() - 24 * 60 * 60 * 1000).getDate()
? ' (Yesterday)'
: ''}
{selectedDay.getDate() === today.getDate() ? ' (Today)' : ''}
{selectedDay.getDate() === new Date(today.getTime() + 24 * 60 * 60 * 1000).getDate()
? ' (Tomorrow)'
: ''}
</small>
</h3>
</div>
<div
bind:this={scrollDiv}
style=" height: {218 + entries * 27.5}px; "
class="w-ful relative -mx-5 overflow-x-scroll overflow-y-hidden md:-ml-[110px]"
>
<canvas
bind:this={canvasElement}
id="weather_week_canvas"
class="border border-border"
style="margin-top: 24px; margin-left: 110px; width: 5000px; height: 200px; "
height="500px"
width="10000px"
></canvas>
<table in:fade class="absolute bottom-0 border-b border-border">
<caption style="display:none"> Weather Week {location.name} </caption>
<tbody>
{#await weather then weather}
<tr>
<th
scope="row"
class="time"
style="color: transparent; padding-left: 4px; background: transparent; left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>Time</th
>
{#each weather.indexes as index, j (j)}
<td
class="time {weather.hourlyTime[index].getDate() === today.getDate() &&
weather.hourlyTime[index].getHours() === today.getHours()
? 'now'
: ''}"
data-date={weather.hourlyTime[index].getDate()}
data-time={weather.hourlyTime[index].getHours() + ':00'}
style="font-size: 11px; position: absolute; bottom: {188 +
27 * entries}px; left:{111 +
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
>{weather.hourlyTime[index].getHours() < 10 ? '0' : ''}{weather.hourlyTime[
index
].getHours()}</td
>
{/each}
</tr>
<!-- icons -->
<tr>
<th
scope="row"
style="color: transparent; padding-left: 4px; background: transparent; left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>Icons</th
>
{#each weather.indexes as index, j (j)}
{@const now =
weather.hourlyTime[index].getDate() === today.getDate() &&
weather.hourlyTime[index].getHours() === today.getHours()}
<td
style="position: absolute; bottom: {27.5 * entries -
24 +
0.8 * 200 -
0.54 *
200 *
((maxTemp - weather.entries[0].values[index]) / diffTemp)}px; left:{116 +
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
><svg class="fill-foreground {now ? 'scale-125' : ''}" width="20px" height="20px">
<use
class="stroke-2"
xlink:href="/images/weather-icons/wi-{weather.hourlyTime[index].getHours() >
6 && weather.hourlyTime[index].getHours() < 21
? 'day'
: 'night'}-{weatherCodes[weatherCodesHourly[index]]}.svg#Layer_1"
></use>
</svg></td
>
{/each}
</tr>
<!-- min / max -->
<tr>
<th
scope="row"
style="color: transparent; padding-left: 4px; background: transparent; left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>Temp graph</th
>
{#each weather.indexes as index, j (j)}
{@const temp = weather.entries[0].values[index]}
{#if !isNaN(temp)}
<td
class={weather.hourlyTime[index].getDate() === today.getDate() &&
weather.hourlyTime[index].getHours() === today.getHours()
? 'now'
: ''}
style="position: absolute; bottom: {27.5 * entries -
49 +
0.8 * 200 -
0.55 * 200 * ((maxTemp - temp) / diffTemp)}px; left:{111 +
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
>{temp.toFixed(0)}</td
>
{/if}
{/each}
</tr>
{#each weather.entries as entry, i (i)}
<tr class="border-t border-border">
<th
scope="row"
class="bg-background text-left"
style="left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>{entry.title}</th
>
{#each weather.indexes as index, j (j)}
{#if !isNaN(entry.values[index])}
<td
class="border-r border-border {weather.hourlyTime[index].getDate() ===
today.getDate() && weather.hourlyTime[index].getHours() === today.getHours()
? 'now'
: ''}"
style="min-width: {5000 / weather.entriesLength}px; max-width: {5000 /
weather.entriesLength}px;
{entry.name === 'temperature_2m'
? 'background: ' +
getColor(
weather.entries[0].values[index].toFixed(0),
$params.temperature_unit
)
: ''};
{entry.name === 'temperature_2m'
? 'color: ' +
(weather.entries[0].values[index] <
($params.temperature_unit === 'celsius' ? -13 : 7) ||
weather.entries[0].values[index] >=
($params.temperature_unit === 'celsius' ? 40 : 104)
? 'white'
: 'black')
: ''};
{entry.name === 'precipitation_probability'
? 'background: rgba(0, 0, 230,' +
weather.entries[2].values[index] / 120 +
')'
: ''};
{entry.name === 'precipitation_probability'
? 'color: ' +
(weather.entries[2].values[index] > 50
? 'white'
: 'hsl(var(--foreground)')
: ''};
{entry.name === 'relative_humidity_2m'
? 'background: rgba(0, 240, 240,' +
weather.entries[4].values[index] ** 3.8 / 10 ** 8.2 +
')'
: ''};"
>{entry.name === 'precipitation' || entry.name === 'temperature_2m'
? entry.values[index].toFixed(1)
: entry.values[index]}</td
>
{/if}
{/each}
</tr>
{/each}
{#if winddir}
<!-- winddir -->
<tr class="border-t border-border">
<th
scope="row"
class="bg-background text-left"
style="z-index: 20; left: 0px; min-width: 110px; max-width: 110px; position: sticky;"
>Wind Dir.</th
>
{#each weather.indexes as index, j (j)}
{#if !isNaN(weather.windDirections[index])}
<td
class="border-r border-border {weather.hourlyTime[index].getDate() ===
today.getDate() && weather.hourlyTime[index].getHours() === today.getHours()
? 'now'
: ''}"
style="transform: rotate({weather.windDirections[
index
]}deg);min-width: {5000 / weather.entriesLength}px; max-width: {5000 /
weather.entriesLength}px;"
><svg class="fill-foreground" width="25px" height="25px">
<use xlink:href="/images/weather-icons/wi-direction-down.svg#Layer_1"></use>
</svg></td
>
{/if}
{/each}
</tr>
{/if}
{/await}
</tbody>
</table>
</div>
</div>
{#await weatherDaily then wd}
{@const sunrise = new Date(Number(wd.daily.sunrise.valuesInt64(selectedDayIndex)) * 1000)}
{@const sunset = new Date(Number(wd.daily.sunset.valuesInt64(selectedDayIndex)) * 1000)}
<div class="mt-6">
<div class="flex items-center gap-1">
<svg class="fill-foreground" width="28px" height="28px">
<use class="stroke-2" xlink:href="/images/weather-icons/wi-sunrise.svg#Layer_1"></use>
</svg>Sunrise: {pad(sunrise.getHours())}:{pad(sunrise.getMinutes())}
</div>
<div class="flex items-center gap-1">
<svg class="fill-foreground" width="28px" height="28px">
<use class="stroke-2" xlink:href="/images/weather-icons/wi-sunset.svg#Layer_1"></use>
</svg>
Sunset: {pad(sunset.getHours())}:{pad(sunset.getMinutes())}
</div>
</div>
{/await}
<div>
<div class="mt-6 flex gap-6 md:mt-12">
<div class="relative w-1/2">
<Select.Root name="model_selection" type="single" bind:value={$params.models}>
<Select.Trigger
aria-label="Forecast days input"
class="h-12 cursor-pointer pt-6 [&_svg]:mb-3">{modelSelected?.label}</Select.Trigger
>
<Select.Content preventScroll={false} class="border-border">
{#each models as mo (mo.value)}
<Select.Item class="cursor-pointer" value={mo.value}>{mo.label}</Select.Item>
{/each}
</Select.Content>
<Label class="absolute top-[0.35rem] left-2 z-10 px-1 text-xs text-muted-foreground"
>Weather model</Label
>
</Select.Root>
</div>
</div>
</div>
</div>
<style>
.now {
font-weight: bold;
}
td {
text-align: center;
font-size: 13px;
}
.weather-week-icon {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
background: #0061a5;
margin: 5px 0;
border-radius: 5px;
}
</style>
+4 -4
View File
@@ -62,12 +62,12 @@ export const load = (async (event) => {
if (location.population && location.population > 543000) {
// 1000 biggest cities
if (event.url.pathname !== `/en/weather/week/${locationRoute}`) {
throw redirect(303, `/en/weather/week/${locationRoute}`);
if (event.url.pathname !== `/weather/week/${locationRoute}`) {
throw redirect(303, `/weather/week/${locationRoute}`);
}
} else {
if (event.url.pathname !== `/en/weather/week/${locationRoute + '_' + location.id}`) {
throw redirect(303, `/en/weather/week/${locationRoute + '_' + location.id}`);
if (event.url.pathname !== `/weather/week/${locationRoute + '_' + location.id}`) {
throw redirect(303, `/weather/week/${locationRoute + '_' + location.id}`);
}
}
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M0 0h144.7l36 254.6-36 257.4H0z"/><path fill="#d80027" d="M367.3 0H512v512H367.3l-29.7-257.3z"/><path fill="#ffda44" d="M144.7 0h222.6v512H144.7z"/><path fill="#d80027" d="M256 354.5V256h66.8v47.3zm-66.8-165.3H256V256h-66.8z"/><path fill="#ff9811" d="M289.4 167a22.3 22.3 0 0 0-33.4-19.3 22.1 22.1 0 0 0-11.1-3c-12.3 0-22.3 10-22.3 22.3H167v111.3c0 41.4 32.9 65.4 58.7 77.8a22.1 22.1 0 0 0-3 11.2 22.3 22.3 0 0 0 33.3 19.3 22.1 22.1 0 0 0 11.1 3 22.3 22.3 0 0 0 19.2-33.5c25.8-12.4 58.7-36.4 58.7-77.8V167zm22.3 111.3c0 5.8 0 23.4-27.5 40.9a136.5 136.5 0 0 1-28.2 13.3c-7-2.4-17.8-6.7-28.2-13.3-27.5-17.5-27.5-35.1-27.5-41v-77.9h111.4z"/></g></svg>

After

Width:  |  Height:  |  Size: 844 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#a2001d" d="M0 0h167l52.3 252L167 512H0z"/><path fill="#eee" d="m167 167 170.8-44.6L512 167v178l-173.2 36.9L167 345z"/><path fill="#6da544" d="M167 0h345v167H167z"/><path fill="#333" d="M167 345h345v167H167z"/></g></svg>

After

Width:  |  Height:  |  Size: 404 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M144.7 0h222.6l37 257.7-37 254.3H144.7l-42.4-255.2z"/><path fill="#496e2d" d="M367.3 0H512v512H367.3z"/><path fill="#333" d="M0 0h144.7v512H0z"/><g fill="#ffda44"><path d="M256 167a89 89 0 1 0 0 178 89 89 0 0 0 0-178zm0 144.7a55.7 55.7 0 1 1 0-111.4 55.7 55.7 0 0 1 0 111.4z"/><path d="M256 222.6c-12.3 0-22.3 10-22.3 22.3v33.4h44.6v-33.4c0-12.3-10-22.3-22.3-22.3z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 577 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#333" d="M0 .4h512l-34 229H36z"/><path fill="#ffda44" d="m367.3 205.3-109.7 19.4-112.9-19.4 45.5-21.3-24.2-44 49.3 9.4 6.3-49.9 34.4 36.7 34.4-36.6 6.3 50L346 140l-24.2 44z"/><path fill="#0052b4" d="M25.6 205.3h466.8L257 439.5z"/><path fill="#eee" d="M34 307.4h446L256 511.6z"/><path fill="#a2001d" d="m0 511.6 256 .4L0 .4zm256 .4 256-.4V0z"/></g></svg>

After

Width:  |  Height:  |  Size: 537 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M256 0h256v512H0V256z"/><path fill="#496e2d" d="M445.2 256.1zm-155.8 0z"/><path fill="#eee" d="M433 293.6a62.4 62.4 0 0 0 12.2-37.5V144.8a55.4 55.4 0 0 1-33.4 11.1 55.6 55.6 0 0 1-44.5-22.2 55.6 55.6 0 0 1-44.5 22.2 55.4 55.4 0 0 1-33.4-11.1v111.3c0 15 5 27.3 12.3 37.5h131.2z"/><path fill="#ff9811" d="M409.8 235.5a91 91 0 0 0 6.3-27.6c0-10.1-13.2-18.3-13.2-18.3s-13.2 8.2-13.2 18.3a91 91 0 0 0 6.3 27.6l-7.6 17.1a38.3 38.3 0 0 0 29 0zm-51.5-55.6a91 91 0 0 0-27 8.3c-8.8 5-9.3 20.5-9.3 20.5s13.7 7.4 22.4 2.3c5.5-3.1 15-11.8 20.8-19.2l18.6-2a38.4 38.4 0 0 0-4.7-14 38.4 38.4 0 0 0-9.7-11.1zm-22.4 72.2a91 91 0 0 0 20.7 19.3c8.8 5 22.5-2.3 22.5-2.3s-.6-15.5-9.3-20.5a91 91 0 0 0-27-8.4l-11.1-15.1a38.4 38.4 0 0 0-9.7 11 38.4 38.4 0 0 0-4.8 14z"/><path fill="#338af3" d="M299 289.5c20.7 33.3 68.3 44.5 68.3 44.5s47.6-11.2 68.4-44.5H298.9z"/><path fill="#eee" d="M256 0H100.2L66.7 14.9 33.4 0H0v33.4l15.7 31.3L0 100.2V256h33.4l34-13.6 32.8 13.6h33.4v-75.2l75.2 75.2H256l-7.7-21.3 7.7-10.2v-15.7l-75.2-75.2H256v-33.4l-11.8-33.4L256 33.4z"/><path fill="#d80027" d="m256 224.5-91-91h-31.4L256 256z"/><path fill="#d80027" d="M33.4 0v33.4H0v66.8h33.4V256h66.8V100.2H256V33.4H100.2V0z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h512v512H0z"/><path fill="#333" d="M400.7 190H308a33.3 33.3 0 0 0-24.2-56.4 33.3 33.3 0 0 0-27.8 14.9 33.4 33.4 0 1 0-52 41.5h-92.7a45.8 45.8 0 0 0 46 44.5h-1.5c0 24.6 20 44.6 44.5 44.6 0 8 2.1 15.4 5.8 21.8l-37 37 28.4 28.3 40.2-40.2a30.5 30.5 0 0 0 4.9 1.4l-24.3 54.8L256 423l37.7-40.8-24.3-54.8a30.4 30.4 0 0 0 4.9-1.4l40.2 40.2 28.3-28.3-37-37a44.2 44.2 0 0 0 5.9-21.8c24.5 0 44.5-20 44.5-44.6h-1.5c24.6 0 46-19.9 46-44.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 639 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="m0 166.9 253-26.7L512 167v178l-261.1 26L0 344.8z"/><path fill="#d80027" d="M0 0h512v166.9H0z"/><path fill="#ff9811" d="M0 344.9h512V512H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 347 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h512v256l-253 36.6L0 256z"/><path fill="#333" d="M0 256h512v256H0z"/><g fill="#ffda44"><path d="m220.9 203.6 21.7 15.8-8.3 25.5L256 229l21.7 15.7-8.3-25.5 21.7-15.7h-26.8L256 178l-8.3 25.5z"/><path d="M320 145.1a127.2 127.2 0 0 0-64-17v33.3a94 94 0 0 1 47.3 12.7 94.7 94.7 0 0 1-94.6 163.8 94 94 0 0 1-31.6-29.8l-27.9 18.4a128.1 128.1 0 0 0 217.7-6.5A128.1 128.1 0 0 0 320 145.1z"/><path d="M182.2 233.7a33.4 33.4 0 0 0 13.3 45.4l108.4 59.2c-7.4 13.5-3.4 30 10 37.3l29.3 16a27.8 27.8 0 0 0 37.8-11l16-29.3z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 723 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#338af3" d="M0 0h512v512H0z"/><path fill="#eee" d="m135 343-41-70 17-38-40-51-9-37 74 51 45-11 19-67 50-29 75 11 87 45 4 74 28 10v76l-53 94-64 20-59-14 15-25-7-26-8 7z"/></g></svg>

After

Width:  |  Height:  |  Size: 364 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#338af3" d="M0 0h512v144.7L488 256l24 111.3V512H0V367.3L26 256 0 144.7z"/><path fill="#eee" d="M0 144.7h512v222.6H0z"/><path fill="#ffda44" d="m332.4 256-31.2 14.7 16.7 30.3-34-6.5-4.2 34.3-23.7-25.2-23.6 25.2-4.3-34.3-34 6.5 16.6-30.3-31.2-14.7 31.3-14.7L194 211l34 6.5 4.3-34.3 23.6 25.2 23.6-25.2 4.4 34.3 34-6.5-16.7 30.3z"/></g></svg>

After

Width:  |  Height:  |  Size: 523 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M512 20.4V490L23.8 255.8z"/><path fill="#a2001d" d="M445.2 246.5h-30.5c8-9.6 7.5-23.7-1.5-32.7a24.2 24.2 0 0 0 0-34.2l-.5.5a25 25 0 0 0 .5-34.8l-137 137a23.9 23.9 0 0 0 34 0l2.6-2.5 65.6-6v28.3h22.3v-30.2l33.4-3z"/><path fill="#ffda44" d="M278.3 311.7 256 300.5l22.3-11.1H423v22.3z"/><path fill="#0052b4" d="M0 0v512h512L28.7 256.2 512 0z"/><path fill="#d80027" d="M512 0 0 256l512 256v-22L43.8 256 512 20.4z"/></g></svg>

After

Width:  |  Height:  |  Size: 614 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h512v167l-23.2 89.7L512 345v167H0V345l29.4-89L0 167z"/><path fill="#eee" d="M0 167h512v178H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 306 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M256 .1h256V512H0V256z"/><path fill="#eee" d="m154.4 300.4 14 29.4 31.8-7.3-14.2 29.3 25.5 20.2-31.8 7.2.1 32.5-25.4-20.3-25.4 20.3v-32.5L97.4 372l25.5-20.2-14.2-29.3 31.7 7.3zm228.9 55.7 7 14.7 15.9-3.7-7.1 14.6 12.7 10.2-15.9 3.5v16.3l-12.6-10.2-12.7 10.2v-16.3l-15.9-3.5 12.8-10.2-7.1-14.6 15.9 3.7zm-65.4-155.8 7 14.7 16-3.6-7.2 14.6 12.8 10-16 3.7.1 16.3-12.7-10.2-12.7 10.2v-16.3l-15.8-3.6 12.7-10-7-14.7 15.8 3.6zm65.4-89 7 14.7 15.9-3.7L399 137l12.7 10-15.9 3.7V167l-12.6-10.2-12.7 10.2v-16.3l-15.9-3.6 12.8-10.1-7.1-14.7 15.9 3.7zm57 66.8 7 14.7 16-3.7-7.1 14.7 12.7 10-15.9 3.7v16.2l-12.6-10.1-12.7 10v-16.1l-15.9-3.6 12.8-10.1-7.1-14.7 15.8 3.7zM399.6 256l5.6 17H423l-14.5 10.4 5.5 17-14.5-10.5-14.4 10.5 5.5-17-14.5-10.5h18zM256 .1H100.2L66.7 15 33.4 0H0v33.4l15.7 31.3L0 100.3v155.8h33.4l34-13.6 32.8 13.6h33.4v-75.2l75.2 75.2H256l-7.7-21.3 7.7-10.2v-15.7l-75.2-75.2H256v-33.4l-11.8-33.4L256 33.5z"/><path fill="#d80027" d="m256 224.6-91-91h-31.4L256 256.1z"/><path fill="#d80027" d="M33.4.1v33.4H0v66.8h33.4v155.8h66.8V100.3H256V33.5H100.2V.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#ffda44" d="m0 322.8 253.6-18.4L512 322.8v33.4l-258 15.3L0 356.2zm0 66.8 257.2-13.8L512 389.6V423l-253 16.9L0 423z"/><path fill="#338af3" d="M0 0h512v322.8H0zm0 356.2h512v33.4H0zM0 423h512v89H0z"/><path fill="#eee" d="m117.3 161.5-50-22.1 50-22 22-50.1 22.2 50 50 22-50 22.2-22.1 50z"/><path fill="#d80027" d="m139.4 94.9 13.6 30.9 31 13.6-31 13.6-13.6 31-13.6-31-31-13.6 31-13.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 577 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M0 0h100.2l68.3 40.7L233.7 0H512v189.2l-45.5 66 45.5 68.6V512H233.7l-65.8-39.2-67.7 39.2H0V322.8l45.6-67.5L0 189.2z"/><path fill="#ffda44" d="M100.2 0v189.2H0v33.4l23 34-23 32.8v33.4h100.2V512h33.4l33.9-22.6 32.8 22.6h33.4V323.8H512v-34.4l-24.2-32.2 24.2-34.6v-33.4H233.7V0h-33.4l-32.6 20-34.1-20z"/><path fill="#d80027" d="M133.6 0v222.6H0v66.8h133.6V512h66.7V289.4H512v-66.8H200.3V0h-66.7z"/></g></svg>

After

Width:  |  Height:  |  Size: 600 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="m0 166.9 253-31.8 259 31.8v178l-257.5 37.4L0 345z"/><path fill="#338af3" d="M0 0h512v166.9H0z"/><path fill="#6da544" d="M0 344.9h512V512H0z"/><g fill="#eee"><path d="M261.6 328.2a72.3 72.3 0 1 1 34.4-136 89 89 0 1 0 0 127.3 72 72 0 0 1-34.4 8.7z"/><path d="m317.2 206 9.6 26.8 25.8-12.3-12.2 25.8 26.9 9.6-27 9.6 12.3 25.8-25.8-12.3-9.6 27-9.6-27-25.8 12.3 12.3-25.8-27-9.6 27-9.6-12.3-25.8 25.8 12.3z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 614 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#ffda44" d="M0 0h445.3l33.9 255-33.9 257-323.7-134.3L0 66.8z"/><path fill="#0052b4" d="M0 66.8V512h445.4z"/><path fill="#0052b4" d="M445.3 0H512v512h-66.7z"/><path fill="#eee" d="m354.6 456-8.3 25.6h-26.8l21.7 15.8-8.3 25.5 21.7-15.8 21.7 15.8-8.3-25.5 21.7-15.8h-26.8zm-55-55.4-8.3 25.5h-26.8l21.7 15.8-8.3 25.5 21.7-15.8 21.7 15.8-8.3-25.5 21.7-15.8h-26.8zM244.4 345l-8.3 25.5h-26.8l21.7 15.8-8.3 25.5 21.7-15.8 21.7 15.8-8.3-25.5 21.7-15.8h-26.8zm-55.1-55.7-8.3 25.5h-26.8l21.7 15.8-8.3 25.5 21.7-15.8L211 356l-8.3-25.5 21.7-15.8h-26.8zm-55.4-55.7-8.3 25.5H98.8l21.7 15.8-8.3 25.5 21.7-15.8 21.7 15.8-8.3-25.5L169 259h-26.8zM78.7 178l-8.3 25.5H43.6l21.7 15.8-8.3 25.5L78.7 229l21.7 15.8-8.3-25.5 21.7-15.8H87zm-55.2-55.7-8.3 25.5h-26.8l21.7 15.8L1.8 189l21.7-15.8L45.2 189l-8.3-25.5 21.7-15.8H31.8z"/></g></svg>

After

Width:  |  Height:  |  Size: 998 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M0 0h144.8l112.9 36.7L367.4 0H512v512H367.4l-108.9-38.1L144.8 512H0z"/><path fill="#ffda44" d="M144.8 0h222.6v512H144.8z"/><path fill="#333" d="m334.1 155.8 14.8 7.5-14.9-7.5-15-7.4c-.8 1.8-20.3 41.4-23.5 102h-22.7v-94.6l-16.7-22.2-16.7 22.2v94.6h-22.7a278.3 278.3 0 0 0-23.6-102l-29.8 14.9c.2.4 20.5 41.7 20.5 103.8v16.7h55.6v94.6h33.4v-94.6h55.6v-16.7c0-32 5.6-58.6 10.3-75.1 5-18 10.2-28.6 10.3-28.7l-15-7.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 620 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#496e2d" d="M0 0h512v512H0z"/><circle cx="200.3" cy="256" r="111.3" fill="#d80027"/></g></svg>

After

Width:  |  Height:  |  Size: 278 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#333" d="M0 0h167l38.2 252.6L167 512H0z"/><path fill="#d80027" d="M345 0h167v512H345l-36.7-256z"/><path fill="#ffda44" d="M167 0h178v512H167z"/></g></svg>

After

Width:  |  Height:  |  Size: 338 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h512v256l-255.2 48L0 256z"/><path fill="#6da544" d="M0 256h512v256H0z"/><path fill="#ffda44" d="m256 167 19.3 59.5H338l-50.6 36.8 19.3 59.5L256 286l-50.6 36.8 19.3-59.5-50.6-36.8h62.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 397 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#496e2d" d="m0 166.9 258-31.7 254 31.7v178l-251.4 41.3L0 344.9z"/><path fill="#eee" d="M0 0h512v166.9H0z"/><path fill="#d80027" d="M0 344.9h512V512H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 347 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M0 0h182.5l88.1 268.5-88 243.5H0z"/><path fill="#d80027" d="m182.5 0-82.3 42.7 82.3 42.7-82.3 42.6 82.3 42.7-82.3 42.7 82.3 42.6-82.3 42.7 82.3 42.7-82.3 42.6 82.3 42.7-82.3 42.7 82.3 42.6H512V0H182.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 407 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M0 0h47.2l207.5 30L464.8 0H512v47.2L477.4 256 512 464.8V512h-47.2l-209.1-35.8L47.2 512H0v-47.2l32.8-202.7L0 47.2z"/><path fill="#d80027" d="M47.2 0 256 208.8 464.8 0H47.2zM256 303.2 47.2 512h417.6L256 303.2z"/><path fill="#6da544" d="M0 47.2v417.6L208.8 256 0 47.2zm512 0L303.2 256 512 464.8V47.2z"/><circle cx="256" cy="256" r="111.3" fill="#eee"/><path fill="#d80027" d="m256 178 9.6 16.8H285l-9.6 16.7 9.6 16.7h-19.3l-9.6 16.7-9.6-16.7H227l9.6-16.7-9.6-16.7h19.3zm-49 78 9.6 16.7H236l-9.6 16.7 9.6 16.7h-19.3l-9.6 16.7-9.6-16.7H178l9.6-16.7-9.6-16.7h19.3zm98 0 9.6 16.7H334l-9.6 16.7 9.6 16.7h-19.3l-9.6 16.7-9.6-16.7H276l9.6-16.7-9.6-16.7h19.3z"/></g></svg>

After

Width:  |  Height:  |  Size: 854 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#6da544" d="M0 0h189.2l54 257.6-54 254.4H0z"/><path fill="#ffda44" d="M189.2 0H512v256l-159 53.5L189.1 256z"/><path fill="#d80027" d="M189.2 256H512v256H189.2z"/></g></svg>

After

Width:  |  Height:  |  Size: 356 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M0 0h512v512H0z"/><path fill="#acabb1" d="M167 178a28 28 0 0 0-28 28H55a28 28 0 0 0 28 28 28 28 0 0 0 28 28 28 28 0 0 0 28 28h234a28 28 0 0 0 28-28 28 28 0 0 0 28-28 28 28 0 0 0 28-28h-84a28 28 0 0 0-28-28z"/><path fill="#ffda44" d="M123 357h44v44h-44zm222 0h44v44h-44zm-178 11h178v44H167zm67-268v33.5L223 145l-12-6v-17h-44v56l89 14 89-14v-56h-44v17l-12 6-11-11.5V100z"/><path fill="#0052b4" d="M167 178v112c0 68 89 88.5 89 88.5s89-20.4 89-88.5V178z"/><path fill="#d80027" d="M167 222.1h178v69H167z"/></g></svg>

After

Width:  |  Height:  |  Size: 704 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M256 0h256v512H0V256z"/><path fill="#eee" d="M0 0h33.4l31.8 16.4 35-16.4H256v133.6l-9.3 33.7 9.3 41.5V256h-47.2l-39.3-7-35.9 7.1L0 256V100.2l15.4-34.5L0 33.4z"/><path fill="#496e2d" d="M445.2 256.1zm-155.8 0z"/><path fill="#d80027" d="m267 235.5-102-102h-31.4L267 267z"/><path fill="#d80027" d="M33.4 0v33.4H0v66.8h33.4v170.6h66.8V100.2h170.2V33.4H100.2V0z"/><path fill="#0052b4" d="M180.8 133.6H256v75.2zm-47.2 47.2v75.3l75.2-.1z"/><path fill="#eee" d="M289.4 133.6V256c0 59.6 155.8 59.6 155.8 0V133.6z"/><path fill="#6da544" d="M289.4 256c0 59.6 77.9 78 77.9 78s78-18.4 78-78h-156z"/><path fill="#a2001d" d="m367.3 207-36.2 15.6V256l36.2 22.3 36.2-22.3v-33.4z"/><path fill="#338af3" d="M331.1 189.2h72.4v33.4H331z"/></g></svg>

After

Width:  |  Height:  |  Size: 924 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#ffda44" d="M0 0h512v326.7l-19.3 76.5 19.3 77.7V512H0V185.2l21.4-76.5L0 31z"/><path fill="#eee" d="M0 31v117.2l512 295.7V326.7L0 31z"/><path fill="#333" d="M0 108.2v77L512 481v-77L0 108.2z"/><g fill="#d80027"><path d="M328.3 228.2a72.3 72.3 0 1 1-136-34.4 89 89 0 1 0 127.3 0 72 72 0 0 1 8.7 34.4z"/><path d="M239.3 144.7h33.4v167h-33.4z"/><path d="M311.6 178H200.4c0 7.8 6.6 14 14.3 14h-.4a14 14 0 0 0 13.9 14 14 14 0 0 0 13.9 13.8h27.8a14 14 0 0 0 14-13.9 14 14 0 0 0 13.8-13.9h-.4c7.6 0 14.3-6.2 14.3-13.9zM178.1 322.9h155.8v33.4H178.1z"/><path d="M289.4 333.9h66.8v33.4h-66.8zm-133.6 0h66.8v33.4h-66.8z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 807 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#ffda44" d="m0 167 252.9-29.3L512 167v178l-255.7 25.7L0 345z"/><path fill="#d80027" d="M0 0h512v167H0z"/><path fill="#6da544" d="M0 345h512v167H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 343 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M113.7 119.8 276 0h236v31.7L306 289.5 31.6 512H0V276z"/><path fill="#ffda44" d="M0 0v276L276 0H0z"/><path fill="#0052b4" d="M512 31.7 31.7 512H512V31.7z"/><path fill="#333" d="m255 245.7 22.1-12-22-12a78 78 0 0 0-65-65l-12-22-12 22a78 78 0 0 0-65 65l-22 12 22 12a78 78 0 0 0 65 65l12 22.1 12-22a78 78 0 0 0 65-65zm-77 32.6a44.5 44.5 0 1 1 0-89 44.5 44.5 0 0 1 0 89z"/><path fill="#d80027" d="m178 200.3 9.7 16.7H207l-9.6 16.7 9.6 16.7h-19.3l-9.6 16.7-9.7-16.7h-19.2l9.6-16.7-9.6-16.7h19.2z"/></g></svg>

After

Width:  |  Height:  |  Size: 695 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h512v256H0z"/><path fill="#0052b4" d="M0 256h512v256H0z"/><path fill="#eee" d="M0 256 256 0l256 256-256 256z"/><path fill="#ffda44" d="m256 133.6 27.6 85H373L300.7 271l27.6 85-72.3-52.5-72.3 52.6 27.6-85-72.3-52.6h89.4z"/></g></svg>

After

Width:  |  Height:  |  Size: 432 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M0 44.6 255.3 18 512 44.6v194.7L493.7 256l18.3 16.7v194.7l-255.5 27.5L0 467.4V272.7l17.5-17.3L0 239.3z"/><path fill="#eee" d="m100.2 256.1 155.8-98 155.8 98-155.8 98z"/><path fill="#6da544" d="M167 300.6h200.3l-44.5-66.8-22.3 22.3-22.2-11-22.3 33.3h-44.5l-22.3-22.3z"/><path fill="#ffda44" d="m256 189.3 5.5 17h18l-14.6 10.5 5.6 17-14.5-10.5-14.5 10.5 5.6-17-14.5-10.5h17.9z"/><path fill="#d80027" d="M0 0v44.6h239.4v96l-157 98.7H0v33.4h82l157.3 99v95.7H0V512h512v-44.6H272.7v-95.8L430 272.7h82v-33.4h-82.4l-156.9-98.6v-96H512V0H0zm256 169.6 137.6 86.5L256 342.6l-137.6-86.5L256 169.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 794 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M113.7 119.8 276 0h236v31.7L306 289.5 31.6 512H0V276z"/><path fill="#ffda44" d="M0 0v276L276 0H0z"/><path fill="#0052b4" d="M512 31.7 31.7 512H512V31.7z"/><path fill="#333" d="m255 245.7 22.1-12-22-12a78 78 0 0 0-65-65l-12-22-12 22a78 78 0 0 0-65 65l-22 12 22 12a78 78 0 0 0 65 65l12 22.1 12-22a78 78 0 0 0 65-65zm-77 32.6a44.5 44.5 0 1 1 0-89 44.5 44.5 0 0 1 0 89z"/><path fill="#d80027" d="m178 200.3 9.7 16.7H207l-9.6 16.7 9.6 16.7h-19.3l-9.6 16.7-9.7-16.7h-19.2l9.6-16.7-9.6-16.7h19.2z"/></g></svg>

After

Width:  |  Height:  |  Size: 695 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#6da544" d="M0 0h512v512H0z"/><path fill="#ffda44" d="M256 100.2 467.5 256 256 411.8 44.5 256z"/><path fill="#eee" d="M174.2 221a87 87 0 0 0-7.2 36.3l162 49.8a88.5 88.5 0 0 0 14.4-34c-40.6-65.3-119.7-80.3-169.1-52z"/><path fill="#0052b4" d="M255.7 167a89 89 0 0 0-41.9 10.6 89 89 0 0 0-39.6 43.4 181.7 181.7 0 0 1 169.1 52.2 89 89 0 0 0-9-59.4 89 89 0 0 0-78.6-46.8zM212 250.5a149 149 0 0 0-45 6.8 89 89 0 0 0 10.5 40.9 89 89 0 0 0 120.6 36.2 89 89 0 0 0 30.7-27.3A151 151 0 0 0 212 250.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 686 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#338af3" d="M0 0h512v167l-37.4 89 37.4 89v167H0l49.6-252z"/><path fill="#ffda44" d="M108.3 167H512v178H108.3z"/><path fill="#333" d="M0 0v512l256-256L0 0z"/></g></svg>

After

Width:  |  Height:  |  Size: 351 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#ffda44" d="M0 0h512L281 293.2 0 512z"/><path fill="#ff9811" d="M506.7 0 0 506.7v5.3h512V0h-5.3z"/><path fill="#eee" d="M398.2 142V97.7h-85.3l-6.5 6.5c-23.2 23.2-19.3 50.2-16.5 70 2.7 18.3 3 25.3-3.8 32-6.8 7-14 6.6-32.2 4-19.7-3-46.8-6.8-70 16.4-23.2 23.2-19.3 50.3-16.4 70 2.6 18.3 3 25.4-4 32.2-6.7 6.8-13.7 6.5-32 3.8-7.2-1-14.5-2-22.4-2.2l-.5 44.6c5 0 10.6.8 16.5 1.7 7 1 15 2.2 23.5 2.2 8.7 0 18-1.3 27-5.2v35.6h66.8v-33.4H209v-22.2h22.3v-33.4h-18c.9-10.8-.6-21.2-2-30-2.5-18.4-3-25.4 4-32.2 6.8-6.8 13.8-6.5 32.2-3.8 14.3 2 32.6 4.7 50.4-3v35.6h66.8v-33.4h-33.3V231h22.3v-33.4h-17.8a130 130 0 0 0-2-30c-1.8-12.8-2.5-20-.4-25.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 831 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h100.2l66.1 53.5L233.7 0H512v189.3L466.3 257l45.7 65.8V512H233.7l-68-50.7-65.5 50.7H0V322.8l51.4-68.5-51.4-65z"/><path fill="#eee" d="M100.2 0v189.3H0v33.4l24.6 33L0 289.5v33.4h100.2V512h33.4l30.6-26.3 36.1 26.3h33.4V322.8H512v-33.4l-24.6-33.7 24.6-33v-33.4H233.7V0h-33.4l-33.8 25.3L133.6 0z"/><path fill="#0052b4" d="M133.6 0v222.7H0v66.7h133.6V512h66.7V289.4H512v-66.7H200.3V0z"/></g></svg>

After

Width:  |  Height:  |  Size: 592 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#338af3" d="M0 0h512v178l-31 76.9 31 79.1v178H0V334l37-80.7L0 178z"/><path fill="#333" d="m0 211.5 256-19.2 256 19.2v89l-254.6 20.7L0 300.5z"/><path fill="#eee" d="M0 178h512v33.5H0zm0 122.5h512V334H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 398 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M0 0h155.8l35 254.6-35 257.4H0z"/><path fill="#a2001d" d="M155.8 0H512v345.1l-183 37.4-173.2-37.4z"/><path fill="#6da544" d="M155.8 345.1H512V512H155.8z"/><path fill="#a2001d" d="M50 .2 22.3 50l27.8 50.4L77.9 50zm55.8 0L78 50l27.7 50.4 28-50.4zM50 137.5l-27.7 49.6 27.8 50.5 27.7-50.5zm55.8 0L78 187.1l27.7 50.5 28-50.5zM50 274.7l-27.7 49.7 27.8 50.4 27.8-50.4zm55.8 0L78 324.4l27.7 50.4 28-50.4zM50 412l-27.7 49.6 27.8 50.5 27.7-50.5zm55.8 0L78 461.6l27.7 50.5 28-50.5z"/></g></svg>

After

Width:  |  Height:  |  Size: 676 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="m0 44.5 257.8-23.7L512 44.5v423l-252.5 26L0 467.5z"/><circle cx="256" cy="256" r="122.4" fill="#eee"/><circle cx="256" cy="256" r="100.2" fill="#6da544"/><circle cx="256" cy="256" r="66.8" fill="#eee"/><path fill="#0052b4" d="M256 239.3 219.8 256v27.8l36.2 22.3 36.2-22.3V256z"/><path fill="#ffda44" d="M219.8 222.6h72.4V256h-72.4z"/><path fill="#a2001d" d="M0 0h512v44.5H0zm0 467.5h512V512H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 602 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M0 61.2 80.7 0h16l18.5 6 12.9-6h94.6l33.8 19.2 33-19.2h63.1l11.9 7.3L384 0h47.2L512 61.2v33.4L491.3 128l20.7 33.4v33.4l-22 29.7 22 31.5v13.1l-9.7 19 9.7 15.3v32.3l-12.7 19.8L512 370v32.8l-28.4 57-446.2-1-37.3-56V370l13.4-13.7L0 335.7v-32.3l14.2-14.2L0 269v-13l27.4-30.6L0 194.8v-33.4l25.5-33.5L0 94.6z"/><path fill="#0052b4" d="M0 0v61.2h142.1L80.7 0H0zm431.3 0-61.2 61.2H512V0h-80.7zM0 194.8V256h80.9l61.2-61.2H0zm370.1 0 61.2 61.2H512v-61.2H370.1zM189.3 242l-14 14h14v-14zm133.6 0v14h14l-14-14zM0 269.1v34.3h.1c21.3 0 21.3 19.5 42.6 19.5 21.4 0 21.4-19.5 42.7-19.5 21.3 0 21.3 19.5 42.7 19.5 21.4 0 21.3-19.5 42.6-19.5 21.4 0 21.4 19.5 42.7 19.5 21.3 0 21.3-19.5 42.7-19.5 21.4 0 21.4 19.5 42.7 19.5 21.3 0 21.3-19.5 42.7-19.5 21.3 0 21.3 19.5 42.6 19.5 21.4 0 21.4-19.5 42.7-19.5 21.3 0 21.3 19.5 42.7 19.5 21.4 0 21.3-19.4 42.5-19.5v-34.3c-21.2 0-21.2 19.5-42.5 19.5s-21.4-19.5-42.7-19.5c-21.3 0-21.3 19.5-42.7 19.5-21.3 0-21.3-19.5-42.6-19.5-21.4 0-21.4 19.5-42.7 19.5-21.3 0-21.3-19.5-42.7-19.5-21.4 0-21.4 19.5-42.7 19.5-21.3 0-21.3-19.5-42.7-19.5-21.3 0-21.3 19.5-42.6 19.5s-21.4-19.5-42.7-19.5c-21.3 0-21.3 19.5-42.7 19.5-21.3 0-21.3-19.5-42.6-19.5H0zm0 66.6V370h.1c21.4 0 21.4 19.5 42.7 19.5 21.3 0 21.3-19.5 42.7-19.5 21.3 0 21.3 19.5 42.6 19.5 21.4 0 21.4-19.5 42.7-19.5 21.3 0 21.3 19.5 42.7 19.5 21.4 0 21.3-19.5 42.6-19.5 21.4 0 21.4 19.5 42.7 19.5 21.3 0 21.3-19.5 42.7-19.5 21.4 0 21.4 19.5 42.7 19.5 21.3 0 21.3-19.5 42.7-19.5 21.3 0 21.3 19.5 42.6 19.5s21.4-19.4 42.5-19.5v-34.3c-21.1.1-21.2 19.5-42.5 19.5s-21.3-19.5-42.6-19.5c-21.4 0-21.4 19.5-42.7 19.5-21.3 0-21.3-19.5-42.7-19.5-21.4 0-21.4 19.5-42.7 19.5-21.3 0-21.3-19.5-42.7-19.5-21.3 0-21.3 19.5-42.6 19.5s-21.4-19.5-42.7-19.5c-21.3 0-21.3 19.5-42.7 19.5-21.3 0-21.3-19.5-42.6-19.5-21.4 0-21.4 19.5-42.7 19.5-21.3 0-21.3-19.5-42.7-19.5H0zm0 67.1V512h92.6l168.1-21.3L419.4 512H512V402.8c-21.2 0-21.2 19.5-42.5 19.5s-21.4-19.5-42.7-19.5c-21.3 0-21.3 19.5-42.7 19.5-21.3 0-21.3-19.5-42.6-19.5-21.4 0-21.4 19.5-42.7 19.5-21.3 0-21.3-19.5-42.7-19.5-21.4 0-21.4 19.5-42.7 19.5-21.3 0-21.3-19.5-42.7-19.5-21.3 0-21.3 19.5-42.6 19.5s-21.4-19.5-42.7-19.5c-21.3 0-21.3 19.5-42.7 19.5-21.3 0-21.3-19.5-42.6-19.5H0z"/><path fill="#d80027" d="m96.7 0 61.2 61.2h31.4L128.1 0H96.7zm126 0v94.6H0v66.8h222.6V256h66.8v-94.6H512V94.6H289.5V0h-66.8zm129.9 0-29.7 29.7v31.5L384.1 0h-31.5zM189.4 194.8 128.1 256h31.5l29.8-29.7v-31.5zm133.5 0 61.2 61.2h31.5l-61.2-61.2h-31.5z"/><path fill="#ffda44" d="m201.9 345-10 78.7-77.8-14.9 37.7 68.7A268.2 268.2 0 0 0 92.6 512h326.8a268.2 268.2 0 0 0-59-34.5l37.7-68.7-77.9 15-9.9-78.8-54.2 58-54.2-58z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h144.7l113.2 39.9L367.3 0H512v512H367.3l-108-45.3L144.8 512H0z"/><path fill="#eee" d="M144.7 0h222.6v512H144.7z"/><path fill="#d80027" d="M300.5 289.4 345 267l-22.2-11v-22.3L278.3 256l22.2-44.5h-22.2L256 178l-22.3 33.4h-22.2l22.2 44.5-44.5-22.3V256L167 267.1l44.5 22.3-11.2 22.3H245V345h22v-33.3h44.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 514 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#6da544" d="M0 0h512v512H0z"/><g fill="#ffda44"><path d="m393 367.3 7 14.7 15.9-3.7-7.1 14.7 12.7 10-15.9 3.7V423L393 412.8 380.3 423v-16.3l-15.9-3.6 12.8-10.1-7.1-14.7L386 382zm-65.4-155.8 7 14.7 16-3.7-7.2 14.7 12.8 10-16 3.6.1 16.3-12.7-10.2-12.7 10.2v-16.3l-15.8-3.5 12.7-10.1-7-14.7 15.8 3.7zm65.4-89 7 14.6 15.9-3.6-7.1 14.6 12.7 10.1-15.9 3.6v16.3L393 167.9l-12.7 10.2v-16.3l-15.9-3.6 12.8-10-7.1-14.7L386 137zm57 66.7 7 14.7 16-3.7-7.1 14.7 12.7 10.1-15.9 3.6v16.3l-12.6-10.2-12.7 10.2v-16.3l-15.9-3.6 12.8-10.1-7.1-14.7L443 204zm-40.8 78 5.6 17h17.9l-14.5 10.4 5.5 17-14.5-10.5-14.4 10.6 5.5-17-14.5-10.6h18z"/><path d="M283.8 328.3a72.3 72.3 0 1 1 34.4-136 89 89 0 1 0 0 127.3 72 72 0 0 1-34.4 8.7zM161 100.2a44.5 44.5 0 0 0-77.1 0h77zm.1.2-38.7 38.7-38.7-38.7a44.3 44.3 0 0 0-5.8 22 44.5 44.5 0 1 0 89 0 44.3 44.3 0 0 0-5.8-22z"/></g><path fill="#a2001d" d="M111.3 128v37.6a44.6 44.6 0 0 0 11.1 1.4c3.9 0 7.6-.5 11.2-1.4V128h-22.3z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#338af3" d="M0 0h401.9L512 110.3V512H110.3L0 401.9z"/><path fill="#ffda44" d="M401.9 0 0 401.9V449l63 63h47.3L512 110.3V63L449 0z"/><path fill="#d80027" d="M449 0 0 449v63h63L512 63V0h-63z"/><path fill="#ffda44" d="m136.4 78 13.8 42.4H195l-36 26.3 13.7 42.5-36.2-26.3-36 26.3 13.7-42.5L78 120.4h44.7z"/></g></svg>

After

Width:  |  Height:  |  Size: 497 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#ffda44" d="m0 378.4 252.9-28.8L512 378.4V512H322.8L256 481l-66.8 31H0z"/><path fill="#6da544" d="m0 256 249.8-28L512 256v122.4H0z"/><path fill="#eee" d="m0 133.6 255.3-28.3L512 133.6V256H0z"/><path fill="#0052b4" d="M0 0h189.2L256 30l66.8-30H512v133.6H0z"/><path fill="#ffda44" d="m137.7 55.7 6.9 21.2H167L148.9 90l6.9 21.3-18.1-13.1-18 13.1 6.8-21.3-18-13h22.3z"/><path fill="#d80027" d="M189.2 0h133.6v512H189.2z"/></g></svg>

After

Width:  |  Height:  |  Size: 612 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#ffda44" d="m401.9 0-234 172.8L0 401.8V512h110.5L339 336.9l173-226.4V0z"/><path fill="#d80027" d="M512 110.3 110.3 512H512V110.3z"/><path fill="#6da544" d="M0 0v401.9L401.9 0H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 374 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h512v512H0z"/><path fill="#eee" d="M389.6 211.5h-89v-89h-89.1v89h-89v89h89v89h89v-89h89z"/></g></svg>

After

Width:  |  Height:  |  Size: 301 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M167 0h178l31 253.2L345 512H167l-33.4-257.4z"/><path fill="#ff9811" d="M0 0h167v512H0z"/><path fill="#6da544" d="M345 0h167v512H345z"/></g></svg>

After

Width:  |  Height:  |  Size: 338 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#496e2d" d="M445.2 256zm-155.8 0z"/><path fill="#0052b4" d="M256 0h256v512H0V256z"/><path fill="#eee" d="m345 256 4.8 14.6H365l-12.4 9 4.7 14.6-12.4-9-12.4 9 4.8-14.6-12.4-9h15.3zm-63 26 13.7 7 10.9-10.8-2.4 15.1 13.6 7-15.1 2.4-2.4 15.1-7-13.6-15.1 2.4 10.8-10.9zm-26 63 14.6-4.7V325l9 12.4 14.6-4.8-9 12.4 9 12.4-14.6-4.7-9 12.4v-15.3zm26 63 7-13.6-10.8-10.9 15.1 2.4 7-13.6 2.4 15 15.1 2.5-13.6 7 2.4 15-10.9-10.8zm63 26-4.7-14.5H325l12.4-9-4.8-14.6 12.4 9 12.4-9-4.7 14.6 12.4 9h-15.3zm63-26-13.6-7-10.9 10.9 2.4-15.2-13.6-7 15-2.3 2.5-15.1 7 13.6 15-2.4-10.8 10.9zm26-63-14.5 4.8V365l-9-12.4-14.6 4.7 9-12.4-9-12.4 14.6 4.8 9-12.4v15.3zm-26-63-7 13.7 10.9 10.9-15.2-2.4-7 13.6-2.3-15.1-15.1-2.4 13.6-7-2.4-15.1 10.9 10.8z"/><path fill="#496e2d" d="M445.2 256zm-155.8 0z"/><path fill="#eee" d="M256 0H100.2L66.7 14.9 33.4 0H0v33.4l15.7 31.3L0 100.2V256h33.4l34-13.6 32.8 13.6h33.4v-75.2l75.2 75.2H256l-7.7-21.3 7.7-10.2v-15.7l-75.2-75.2H256v-33.4l-11.8-33.4L256 33.4z"/><path fill="#d80027" d="m256 224.5-91-91h-31.4L256 256z"/><path fill="#d80027" d="M33.4 0v33.4H0v66.8h33.4V256h66.8V100.2H256V33.4H100.2V0z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="m0 256 254.5-51.3L512 256v256H0z"/><path fill="#0052b4" d="M0 0h256l52.7 132.8L256 256H0z"/><path fill="#eee" d="M256 0h256v256H256zM152.4 89l16.6 51h53.6l-43.4 31.6 16.6 51-43.4-31.5-43.4 31.5 16.6-51L82.2 140h53.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 425 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M144.8 0h222.4l32 260-32 252H144.8l-32.1-256z"/><path fill="#ffda44" d="m256.1 167 22.1 68h71.5L292 277l22 68-57.8-42-57.9 42 22.1-68-57.8-42H234z"/><path fill="#496e2d" d="M0 0h144.8v512H0z"/><path fill="#ffda44" d="M367.2 0H512v512H367.2z"/></g></svg>

After

Width:  |  Height:  |  Size: 449 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h512v512H0z"/><path fill="#ffda44" d="m140.1 155.8 22.1 68h71.5l-57.8 42.1 22.1 68-57.9-42-57.9 42 22.2-68-57.9-42.1H118zm163.4 240.7-16.9-20.8-25 9.7 14.5-22.5-16.9-20.9 25.9 6.9 14.6-22.5 1.4 26.8 26 6.9-25.1 9.6zm33.6-61 8-25.6-21.9-15.5 26.8-.4 7.9-25.6 8.7 25.4 26.8-.3-21.5 16 8.6 25.4-21.9-15.5zm45.3-147.6L370.6 212l19.2 18.7-26.5-3.8-11.8 24-4.6-26.4-26.6-3.8 23.8-12.5-4.6-26.5 19.2 18.7zm-78.2-73-2 26.7 24.9 10.1-26.1 6.4-1.9 26.8-14.1-22.8-26.1 6.4 17.3-20.5-14.2-22.7 24.9 10.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 705 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="m0 384 255.8-29.7L512 384v128H0z"/><path fill="#0052b4" d="m0 256 259.5-31L512 256v128H0z"/><path fill="#ffda44" d="M0 0h512v256H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 340 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M0 0h512v89l-66.3 167.5L512 423v89H0v-89l69.7-167.3L0 89z"/><path fill="#eee" d="M0 89h512v78l-39.7 91.1L512 345v78H0v-78l36.3-85.6L0 167z"/><path fill="#d80027" d="M0 167h512v178H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 391 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M0 0h512v102.4L483.8 151l28.2 53.8v102.4l-30.5 50.7 30.5 51.7V512H0l39.8-257z"/><path fill="#ff9811" d="M0 44.5v423V256z"/><path fill="#eee" d="M27.2 102.4v102.4H512V102.4H27.2zm0 204.8v102.4H512V307.2H27.2z"/><path fill="#d80027" d="M0 0v512l256-256L0 0z"/><path fill="#eee" d="m103.6 189.2 16.6 51h53.6l-43.4 31.6 16.6 51-43.4-31.5-43.4 31.5 16.6-51-43.4-31.6H87z"/></g></svg>

After

Width:  |  Height:  |  Size: 574 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M0 0h512v256.2l-41.9 64.3 41.9 63.7V512H0V384.2L41.3 320 0 256.2z"/><path fill="#eee" d="M0 256.2h512v42.9l-15.7 21.6 15.7 21v42.5H0v-42.5l15.1-21.5L0 299z"/><path fill="#d80027" d="M0 299.1h512v42.6H0z"/><path fill="#ffda44" d="m182.8 190.4 5.2 16.4h17.1l-13.8 10 5.3 16.3-13.8-10-14 10 5.4-16.3-13.9-10h17.1zm0 213.3L188 420h17.1l-13.8 10 5.3 16.2-13.8-10-14 10L174 430l-14-10h17.2zm-99.2-72.1 5.2 16.2h17.1L92.1 358l5.2 16.2-13.7-10-14 10L75 358l-14-10.1h17.2zm37.9-119.8 5 16h17.2l-13.8 10.3 5.2 16.2-13.7-10-14 10 5.4-16.3-14-10.1H116zm-60.4 67h17l5.5-16.2 5.2 16.2h17.1L92.1 289l5.2 16.4L83.6 295l-14 10.3 5.4-16.4zm46.5 143 5.3-16.2L99 395.4h17.1l5.4-16.2 5.2 16.3h17.1L130 405.6l5.3 16.2-13.8-10zM282 331.6l-5.4 16.2h-17l13.8 10.2-5.3 16.2 13.9-10 13.8 10-5.2-16.3 13.7-10.1h-17zm-38-119.8-5.3 16.2h-17.1l14 10.2-5.4 16.2 13.9-10 13.8 10-5.3-16.3 13.8-10.1h-17zm60.3 67h-17l-5.3-16.2-5.4 16.2h-17l13.8 10.1-5.3 16.4L282 295l13.8 10.3-5.2-16.4zm-46.4 143-5.3-16.2 13.8-10.2h-17l-5.3-16.2-5.4 16.3h-17.1l14 10.1-5.4 16.2 13.9-10z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#0052b4" d="M0 0h512v342.3l-22 34.2 22 32.5v103H0V409l25.4-31L0 342.2z"/><path fill="#eee" d="m175.2 164.2 13.8 42.5h44.7L197.6 233l13.8 42.5-36.2-26.3-36.1 26.3 13.8-42.5-36.2-26.3h44.7zm-76.7-44.5 8.2 25.5h26.9L111.9 161l8.3 25.5-21.7-15.7-21.7 15.7L85 161l-21.7-15.7h26.9z"/><path fill="#ffda44" d="M0 342.3h512V409H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 518 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#6da544" d="m0 0 216.9 301.6L512 512V0z"/><path fill="#0052b4" d="m0 0 512 512H0z"/><circle cx="256" cy="256" r="66.8" fill="#ffda44"/><path fill="#eee" d="m95.3 367.3 7 14.7 16-3.7L111 393l13 10-16 3.6.1 16.3-12.7-10.2L82.6 423v-16.3L66.9 403l12.6-10-7-14.7 15.8 3.7zM49.3 245l7.1 14.7 15.9-3.7-7.1 14.6 12.7 10.2-15.9 3.5v16.3l-12.6-10.2-12.7 10.2v-16.3l-15.9-3.5 12.8-10.2-7.1-14.6 15.8 3.7zm46-100.2 7 14.7 16-3.7-7.2 14.7 12.8 10-16 3.7.1 16.2-12.7-10.1-12.7 10.1v-16.2l-15.8-3.6 12.7-10.1-7-14.7 15.8 3.7zm57.1 78 7 14.6 15.9-3.7-7.1 14.7 12.7 10.1-15.8 3.6v16.3L152.4 268l-12.7 10.2V262l-15.8-3.6 12.7-10.1-7-14.7 15.8 3.7zm-40.8 66.7 5.5 17H135L120.5 317l5.6 17-14.5-10.5L97 333.9l5.6-17-14.5-10.5H106z"/><path fill="#6da544" d="M256 300.5h22.3s9.6-17 0-33.4l22.2-22.2-11.1-22.3h-11.1s-5.6 16.7-27.9 16.7-27.8-16.7-27.8-16.7h-11.1l11.1 22.3-11.1 22.2 11.1 11.2S233.7 256 256 267c0 0 9.5 14 0 33.4z"/><path fill="#ffda44" d="M422.2 140.2a44.7 44.7 0 0 0-30-50.8 83.1 83.1 0 0 1 7.5 49.9 45.6 45.6 0 0 0-10.4-11.6 45.7 45.7 0 0 0-53.7-2.4 85.3 85.3 0 0 1 35 16.7c8.3 6.6 15 15.5 20.2 24.1l-68 21.3c66.8 11.2 122.4-33.4 122.4-33.4-6.1-12.3-15.7-14.4-23-13.8z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M0 0h512v512H0z"/><path fill="#6da544" d="M400.7 222.6h-33.4a111.3 111.3 0 0 1-222.6 0h-33.4c0 66.2 44.5 122 105.2 139.2a37 37 0 0 0 3.9 40.5l36.3-29.2 36.4 29.2a37 37 0 0 0 3.7-40.8 144.8 144.8 0 0 0 103.9-138.9z"/><path fill="#ffda44" d="M167 211.5s0 55.6 55.6 55.6l11.1 11.2H256s11.1-33.4 33.4-33.4c0 0 0-22.3 22.3-22.3H345s-11-44.5 44.6-77.9l-22.3-11.1s-78 55.6-133.6 44.5v22.2h-22.2l-11.2-11-33.3 22.2z"/></g></svg>

After

Width:  |  Height:  |  Size: 613 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M0 0h512v256l-265 45.2z"/><path fill="#d80027" d="M210 256h302v256H0z"/><path fill="#0052b4" d="M0 0v512l256-256L0 0z"/></g></svg>

After

Width:  |  Height:  |  Size: 323 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#ffda44" d="m0 345 256.7-25.5L512 345v167H0z"/><path fill="#d80027" d="m0 167 255-23 257 23v178H0z"/><path fill="#333" d="M0 0h512v167H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 334 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#338af3" d="M0 0h512v256l-153.2 35.7L210 256z"/><path fill="#6da544" d="M210 256h302v256H0z"/><path fill="#eee" d="M0 0v512l256-256z"/><path fill="#d80027" d="m103.6 189.2 16.6 51h53.6l-43.4 31.6 16.6 51-43.4-31.5-43.4 31.5 16.6-51-43.4-31.6H87z"/></g></svg>

After

Width:  |  Height:  |  Size: 442 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M0 0h133.6l32.7 20.3 34-20.3H512v222.6L491.4 256l20.6 33.4V512H200.3l-31.7-20.4-35 20.4H0V289.4l29.4-33L0 222.7z"/><path fill="#eee" d="M133.6 0v222.6H0v66.8h133.6V512h66.7V289.4H512v-66.8H200.3V0h-66.7z"/></g></svg>

After

Width:  |  Height:  |  Size: 412 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#496e2d" d="M0 0h512v512H0z"/><path fill="#ffda44" d="M205.9.5h33.4l16 255-16 255.9h-33.4z"/><path fill="#333" d="M239.3.5h33.4l16.6 256-16.6 254.9h-33.4z"/><path fill="#ffda44" d="M511.4 205.9v33.4l-256 16.1L.6 239.3v-33.4z"/><path fill="#333" d="M511.4 239.3v33.4L255.8 291 .6 272.7v-33.4z"/><path fill="#eee" d="M272.7.5h33.4v510.9h-33.4z"/><path fill="#eee" d="M511.5 272.7v33.4H.6v-33.4z"/><circle cx="256" cy="256" r="122.4" fill="#d80027"/><path fill="#496e2d" d="M283.9 269.7c-9.1-18.3-21-36.7-21-36.7s.3-8.4.3-13.4a13.7 13.7 0 0 0-27.2-1.2 10.8 10.8 0 0 0-4.3 20.3 15.5 15.5 0 0 1 9.2-8.6 13.8 13.8 0 0 0 2.2 1.5l1.8.8s-6.5 18.3-6.5 28.2c0 27.6 18.2 36.1 18.2 36.1v.3l-9.1 9h18.2v-18l9 9c.2 0 17.3-11 9.2-27.2zM256 155.8l4.1 12.8h13.5l-10.9 7.9 4.2 12.7-10.9-7.9-10.8 8 4.1-12.8-10.9-8H252zM197.1 175l10.9 7.8 10.8-7.8-4.1 12.7 10.8 7.9h-13.4l-4.1 12.7-4.2-12.7h-13.4l10.9-7.9zm-36.4 50h13.4l4.2-12.7 4.1 12.7h13.4l-10.8 8 4.1 12.7-10.8-7.9-10.9 7.9 4.2-12.8zm0 62 10.9-8-4.2-12.7 10.9 7.9 10.8-7.9-4.1 12.8 10.8 7.9h-13.4l-4.1 12.7L174 287zm36.4 50 4.2-12.7-10.9-7.9h13.4l4.2-12.7 4.1 12.7h13.4l-10.8 7.9 4.1 12.7-10.8-7.8zm58.9 19.2-4.1-12.8h-13.5l10.9-7.9-4.2-12.7 10.9 7.9 10.8-8-4.1 12.8 10.9 8H260zm58.9-19.2-10.9-7.8-10.8 7.8 4.1-12.7-10.8-7.9h13.4l4.1-12.7 4.2 12.7h13.4l-10.9 7.9zm36.4-50h-13.4l-4.2 12.7-4.1-12.7h-13.4l10.8-8-4.1-12.7 10.8 7.9 10.9-7.9-4.2 12.8zm0-62-10.9 8 4.2 12.7-10.9-7.9-10.8 7.9 4.1-12.8-10.8-7.9h13.4l4.1-12.7L338 225zm-36.4-50-4.2 12.7 10.9 7.9h-13.4l-4.2 12.7-4.1-12.7h-13.4l10.8-7.9-4.1-12.7 10.8 7.8z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="M35.5 256h444l32.5-66.8V0H322.8L256 31.8v444.6L189.2 512H0V322.8z"/><path fill="#0052b4" d="M479.5 256h-444L0 189.2V0h189.2L256 31.8v444.6l66.8 35.6H512V322.8z"/><path fill="#eee" d="M189.2 0h133.6v189.2H512v133.6H322.8V512H189.2V322.8H0V189.2h189.2z"/><path fill="#496e2d" d="M322.8 256a66.8 66.8 0 1 1-133.6 0c0-36.9 66.8-66.8 66.8-66.8s66.8 30 66.8 66.8z"/><path fill="#0052b4" d="M189.2 256a66.8 66.8 0 1 1 133.6 0"/><path fill="#d80027" d="M218.4 222.6v41.7a37.6 37.6 0 1 0 75.2 0v-41.7z"/></g></svg>

After

Width:  |  Height:  |  Size: 701 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#496e2d" d="M0 0h256l45.3 251.8L256 512H0z"/><path fill="#eee" d="M256 0h256v512H256z"/><g fill="#d80027"><path d="m311 206.9-21 29-34-11 21 28.8-21 29 34-11.1 21 29v-35.8l34-11.1-34-11z"/><path d="M277.2 328.3a72.3 72.3 0 1 1 34.5-136 89 89 0 1 0 0 127.3 72 72 0 0 1-34.5 8.7z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 478 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="M0 0h512v512H0z"/><path fill="#d80027" d="M334 211.5v22.2h30.5a134.2 134.2 0 0 1-49.5 42 27.7 27.7 0 0 0-47.2 13.2 134.8 134.8 0 0 1-23.6 0 27.8 27.8 0 0 0-47.2-13.2 134.2 134.2 0 0 1-49.5-42h30.6v-22.2h-78a155.8 155.8 0 0 0 311.7 0H334z"/></g></svg>

After

Width:  |  Height:  |  Size: 443 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#eee" d="m0 167 254.6-36.6L512 166.9v178l-254.6 36.4L0 344.9z"/><path fill="#6da544" d="M0 0h512v166.9H0z"/><path fill="#0052b4" d="M0 344.9h512V512H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 348 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#d80027" d="m0 384 254.7-32.7L512 383.9V512H0z"/><path fill="#0052b4" d="m0 256 255-27 257 27v128H0z"/><path fill="#ffda44" d="M0 0h512v256H0z"/><circle cx="256" cy="256" r="89" fill="#ffda44"/><path fill="#338af3" d="M256 311.6c-30.7 0-55.7-25-55.7-55.6v-33.4a55.7 55.7 0 0 1 111.4 0V256c0 30.6-25 55.6-55.7 55.6z"/><path fill="#333" d="M345 122.4h-66.7a22.3 22.3 0 0 0-44.6 0H167a23 23 0 0 0 23 22.3h-.8c0 12.3 10 22.3 22.3 22.3 0 12.3 10 22.2 22.2 22.2h44.6c12.3 0 22.2-10 22.2-22.2 12.3 0 22.3-10 22.3-22.3h-.8a23 23 0 0 0 23-22.3z"/></g></svg>

After

Width:  |  Height:  |  Size: 732 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><mask id="a"><circle cx="256" cy="256" r="256" fill="#fff"/></mask><g mask="url(#a)"><path fill="#333" d="m0 167 254.6-36.6L512 166.9v178l-254.6 36.4L0 344.9z"/><path fill="#0052b4" d="M0 0h512v166.9H0z"/><path fill="#eee" d="M0 344.9h512V512H0z"/></g></svg>

After

Width:  |  Height:  |  Size: 345 B

Some files were not shown because too many files have changed in this diff Show More