Skip to content

Commit bc819cc

Browse files
Merge pull request #10 from geo-engine/data
data init
2 parents 0e5eaf0 + d3ba7b7 commit bc819cc

30 files changed

+736
-85
lines changed

package-lock.json

Lines changed: 90 additions & 76 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@fontsource/poppins": "^5.2.5",
1414
"@jop-software/astro-cookieconsent": "^3.0.1",
1515
"@tailwindcss/vite": "^4.1.3",
16-
"astro": "^5.7.10",
16+
"astro": "^5.8.0",
1717
"astro-icon": "^1.1.5",
1818
"astro-pagefind": "^1.8.3",
1919
"preline": "^3.0.1",

src/components/Data.astro

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
import {Picture} from 'astro:assets';
3+
import Layout from '../layouts/Layout.astro';
4+
import Section from '../components/Section.astro';
5+
import {useLocalePages, useTranslations, type LocaleString} from '../i18n/utils';
6+
import type {DataEntryMap} from 'astro:content';
7+
import {mdExcerpt} from './utils';
8+
9+
interface Props {
10+
datum: DataEntryMap['data'];
11+
pageBody: string;
12+
lang: LocaleString;
13+
}
14+
15+
const {datum, pageBody, lang} = Astro.props;
16+
17+
const p = useLocalePages(Astro.currentLocale);
18+
const t = useTranslations(lang);
19+
---
20+
21+
<style>
22+
div :global(a) {
23+
color: var(--color-main-green);
24+
}
25+
div :global(a:hover) {
26+
text-decoration: underline;
27+
}
28+
div :global(h2) {
29+
margin-top: 1rem;
30+
margin-bottom: 1rem;
31+
font-size: 1.5rem;
32+
font-weight: s emibold;
33+
}
34+
div :global(ul) {
35+
list-style-type: disc;
36+
padding-left: 1.5rem;
37+
}
38+
div :global(ol) {
39+
list-style-type: decimal;
40+
padding-left: 1.5rem;
41+
}
42+
</style>
43+
44+
<Layout title={datum.title} description={mdExcerpt(pageBody, 200)}>
45+
<Section>
46+
<Picture class="w-full rounded-xl object-cover" src={datum.exampleImage} alt={datum.title} />
47+
<div class="text-lg"><slot /></div>
48+
</Section>
49+
<Section bg="green" box>
50+
<h3 class="mt-6 mb-4 text-xl font-semibold">{t('data.specs')}</h3>
51+
{
52+
datum.classes && (
53+
<>
54+
<h4 class="mt-4 text-lg font-semibold">{t('data.classes')}</h4>
55+
<ul class="list-disc pl-6">
56+
{datum.classes.map((className) => (
57+
<li>{className}</li>
58+
))}
59+
</ul>
60+
</>
61+
)
62+
}
63+
<h4 class="mt-4 text-lg font-semibold">{t('data.dataSources')}</h4>
64+
<ul class="list-disc pl-6">
65+
{datum.dataSources.map((source) => <li>{source}</li>)}
66+
</ul>
67+
<h4 class="mt-4 text-lg font-semibold">{t('data.method')}</h4>
68+
<p>{datum.method}</p>
69+
<h4 class="mt-4 text-lg font-semibold">{t('data.quality')}</h4>
70+
<p>{datum.quality}</p>
71+
{datum.qualityImage && <Picture src={datum.qualityImage} alt={t('data.quality')} />}
72+
<div class="mt-4">
73+
<h4 class="text-lg font-semibold">{t('data.properties')}</h4>
74+
<table class="border-main-lightgreen mt-4 w-full border-2">
75+
<tbody>
76+
<tr>
77+
<th class="border-main-lightgreen border-b px-2 py-1 text-left">{t('data.crs')}</th>
78+
<td class="border-main-lightgreen border-b px-2 py-1">{datum.properties.crs}</td>
79+
</tr>
80+
<tr>
81+
<th class="border-main-lightgreen border-b px-2 py-1 text-left">{t('data.time')}</th>
82+
<td class="border-main-lightgreen border-b px-2 py-1">{datum.properties.time}</td>
83+
</tr>
84+
<tr>
85+
<th class="border-main-lightgreen border-b px-2 py-1 text-left">{t('data.spatialResolution')}</th>
86+
<td class="border-main-lightgreen border-b px-2 py-1">{datum.properties.spatialResolution}</td>
87+
</tr>
88+
<tr>
89+
<th class="px-2 py-1 text-left">{t('data.spatialValidity')}</th>
90+
<td class="px-2 py-1">{datum.properties.spatialValidity}</td>
91+
</tr>
92+
</tbody>
93+
</table>
94+
</div>
95+
</Section>
96+
<Section bg="beach">
97+
<p class="mx-auto pb-8 text-2xl font-medium text-white text-shadow-lg">{t('contact.slogan')}</p>
98+
<p class="mx-auto">
99+
<a
100+
href={p('contact')}
101+
class="text-main-green hover:text-second-torquise inline-flex items-center gap-x-2 rounded-lg border border-transparent bg-white p-4 text-sm font-medium focus:bg-white focus:outline-hidden disabled:pointer-events-none disabled:opacity-50 sm:p-5"
102+
>
103+
{t('contact.button')}
104+
</a>
105+
</p>
106+
</Section>
107+
</Layout>

src/components/NavBar.astro

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ const p = useLocalePages(Astro.currentLocale);
101101
{t('nav.services')}
102102
</a>
103103

104+
<a
105+
class="flex items-center p-2 text-sm text-white focus:text-white focus:outline-hidden"
106+
href={p('data')}
107+
aria-current="page"
108+
>
109+
<Icon name="mdi:database" class="me-3 block size-4 shrink-0 md:me-2 md:hidden" />
110+
{t('data.title')}
111+
</a>
112+
104113
<a
105114
class="flex items-center p-2 text-sm text-white/80 hover:text-white focus:text-white focus:outline-hidden"
106115
href={p('references')}

src/components/News.astro

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import {getCollection} from 'astro:content';
33
import {asLocale, slugToPostLink, useTranslations} from '../i18n/utils';
44
import {Image} from 'astro:assets';
5+
import {mdExcerpt} from './utils';
56
67
const pageLang = Astro.currentLocale;
78
const t = useTranslations(pageLang);
@@ -41,7 +42,7 @@ const newsItems: Array<{
4142
day: 'numeric',
4243
}),
4344
image: page.props.data.image,
44-
abstract: page.props.body.slice(0, 200) + '',
45+
abstract: mdExcerpt(page.props.body, 200),
4546
link: slugToPostLink(page.params.lang, page.params.slug),
4647
};
4748
});

src/components/Post.astro

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import {Picture} from 'astro:assets';
33
import Layout from '../layouts/Layout.astro';
44
import Section from '../components/Section.astro';
5-
import PageHeader from './PageHeader.astro';
65
76
interface Props {
87
date: Date;
@@ -29,11 +28,28 @@ const pageDescription = pageBody.slice(0, 200) + '…';
2928
div :global(a:hover) {
3029
text-decoration: underline;
3130
}
31+
div :global(h2) {
32+
margin-top: 1rem;
33+
margin-bottom: 1rem;
34+
font-size: 1.5rem;
35+
font-weight: s emibold;
36+
}
37+
div :global(ul) {
38+
list-style-type: disc;
39+
padding-left: 1.5rem;
40+
}
41+
div :global(ol) {
42+
list-style-type: decimal;
43+
padding-left: 1.5rem;
44+
}
45+
div :global(pre) {
46+
padding: 1rem;
47+
}
3248
</style>
3349

3450
<Layout title={title} description={pageDescription}>
3551
<Section>
36-
<div class="mx-auto max-w-2xl">
52+
<div class="mx-auto w-full text-justify">
3753
<Picture class="w-full rounded-xl object-cover" src={image} alt={title} />
3854
<p class="my-8 text-xs text-gray-500 dark:text-neutral-500">
3955
{formattedDate}

src/components/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Generates an excerpt from a Markdown-formatted string by removing Markdown headings and links,
3+
* then truncating the result to the specified length and appending an ellipsis.
4+
*
5+
* @param text - The Markdown-formatted input string.
6+
* @param length - The maximum length of the excerpt (in characters).
7+
* @returns A plain text excerpt of the specified length, with Markdown formatting removed and an ellipsis appended.
8+
*/
9+
export function mdExcerpt(text: string, length: number) {
10+
const mdHeadingsAndLinksRegex = /(?:__|[*#])|\[(.*?)\]\(.*?\)|\[(.*?)\]/gm;
11+
const trimmedText = text.replace(mdHeadingsAndLinksRegex, '$1').trim();
12+
13+
if (trimmedText.length <= length) {
14+
return trimmedText; // No need to truncate if already short enough
15+
}
16+
17+
return trimmedText.substring(0, length) + '…';
18+
}

src/content.config.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,28 @@ const cookies = defineCollection({
6464
}),
6565
});
6666

67+
const data = defineCollection({
68+
schema: ({image}) =>
69+
z.object({
70+
title: z.string().nonempty().max(100),
71+
exampleImage: image(),
72+
classes: z.optional(z.array(z.string().nonempty()).min(1)),
73+
dataSources: z.array(z.string().nonempty()).min(1),
74+
method: z.string().nonempty(),
75+
quality: z.string().nonempty(),
76+
qualityImage: z.optional(image()),
77+
properties: z.object({
78+
crs: z.string().nonempty(),
79+
time: z.string().nonempty(),
80+
spatialResolution: z.string().nonempty(),
81+
spatialValidity: z.string().nonempty(),
82+
}),
83+
}),
84+
});
85+
6786
// 4. Export a single `collections` object to register your collection(s)
6887
export const collections = {
88+
data,
6989
cookies,
7090
posts,
7191
publications,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
title: Dominante Baumarten
3+
exampleImage: ../../../images/data/dominant-tree-types.png
4+
classes:
5+
[
6+
'Erlen (Alnus)',
7+
'Birken (Betula)',
8+
'Buchen (Fagus)',
9+
'Fichten (Picea)',
10+
'Kiefern (Pinus)',
11+
'Eichen (Quercus)',
12+
'Baumfrei (no_tree)',
13+
' Andere Nadelbäume (other_coniferous)',
14+
' Andere Laubbäume (other_deciduous)',
15+
]
16+
dataSources:
17+
- Sentinel-2
18+
- SRTM
19+
- OpenAgrar species data
20+
- Copernicus Dominant Leaf Type 2018 (raster 10 m), Europe
21+
method: Gradient-boosted decision trees
22+
quality: 93% accuracy
23+
qualityImage: ../../../images/data/dominant-tree-types-confusion.png
24+
properties:
25+
crs: EPSG:32632 - WGS 84 / UTM zone 32N
26+
time: Trainiert für Deutschland und die Jahre 2017 bis 2018
27+
spatialResolution: 10 Meter
28+
spatialValidity: Deutschland
29+
---
30+
31+
Eine Klassifizierung der vorherrschenden Baumarten pro Sentinel-2-Pixel. Durch die 10 m x 10 m Auflösung von Sentinel-2 kann das Modell keine einzelnen Bäume klassifizieren, sondern bestimmt die dominante Klasse jedes Pixels.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
title: Dominant tree types
3+
exampleImage: ../../../images/data/dominant-tree-types.png
4+
classes:
5+
[
6+
Alders (Alnus),
7+
Birches (Betula),
8+
Beeches (Fagus),
9+
Spruces (Picea),
10+
Pines (Pinus),
11+
Oaks (Quercus),
12+
Treeless (no_tree),
13+
Other conifers (other_coniferous),
14+
Other deciduous trees (other_deciduous),
15+
]
16+
dataSources:
17+
- Sentinel-2
18+
- SRTM
19+
- OpenAgrar species data
20+
- Copernicus Dominant Leaf Type 2018 (raster 10 m), Europe
21+
method: Gradient-boosted decision trees
22+
quality: 93% accuracy
23+
qualityImage: ../../../images/data/dominant-tree-types-confusion.png
24+
properties:
25+
crs: EPSG:32632 - WGS 84 / UTM zone 32N
26+
time: Trained for Germany and years 2017 to 2018
27+
spatialResolution: 10 meters
28+
spatialValidity: Germany
29+
---
30+
31+
A classification of the dominant tree species per Sentinel-2 pixel. Due to the 10 m x 10 m resolution of Sentinel-2, the model cannot classify individual trees but determines the dominant class of each pixel.

0 commit comments

Comments
 (0)