Skip to content

fix(performance): improve tiles for performance #726

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c7af3cb
refactor: rebuild tree-app using nextJS and tailwind
danji90 Apr 8, 2025
f67271b
chore: remove gh sctions
danji90 Apr 8, 2025
d4ea2c4
fix(CookieBanner): add CookieBanner component
danji90 Apr 8, 2025
c5149be
chore: update translation
danji90 Apr 8, 2025
f707a84
chore: fix cookie banner
danji90 Apr 8, 2025
e1682eb
chore: improve cookie banner style
danji90 Apr 8, 2025
c11024e
chore: upgrade tile version
danji90 Apr 8, 2025
0a6f3e9
chore: merge master
danji90 Jul 23, 2025
e5e0ca2
feat(tree type PDF): add PDF viewer for tree types
danji90 Jul 23, 2025
6af9c13
chore: fix tree pdf modal footer OK button
danji90 Jul 23, 2025
309c03b
chore: always redirect to map view on load when there is a map positi…
danji90 Jul 23, 2025
c7db772
chore: try tile build with various tippecanoe options
danji90 Jul 30, 2025
43c0351
chore: merge staging
danji90 Aug 4, 2025
11627f4
chore: clean
danji90 Aug 4, 2025
80fff77
chore: improve tile build
danji90 Aug 7, 2025
f5b4745
chore: remove ft union mbtiles file
danji90 Aug 7, 2025
010985c
chore: zoom in on map on click to load the tiles with forest type data
danji90 Aug 11, 2025
4a8c3d4
chore: agdjust tile build
danji90 Aug 11, 2025
d37430b
chore: merge SZ branch
danji90 Aug 12, 2025
9d186b2
chore: clean
danji90 Aug 12, 2025
ca9a78a
chore: fix generalisation
danji90 Aug 12, 2025
cb181bc
chore(CookieBanner): put cookie banner inside portal
danji90 Aug 12, 2025
c31ef26
Merge branch 'staging' into daniel/ftUnionPolygon
danji90 Aug 12, 2025
8d0754f
chore: merge staging
danji90 Aug 12, 2025
8cdc0ae
chore: update sqlite db
danji90 Aug 12, 2025
a40946b
chore: fix z-indices
danji90 Aug 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 27 additions & 23 deletions components/CookieBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useMemo } from "react";
import { createPortal } from "react-dom";
import { Trans, useTranslation } from "react-i18next";

import useLocalStorage from "@/utils/hooks/useLocalStorage";
Expand All @@ -18,30 +19,33 @@ const CookieBanner = () => {
[i18n.language],
);

return lsValue !== "true" ? (
<Message className="absolute bottom-0 z-50 flex w-screen items-center justify-between rounded-none border-none bg-zinc-900 text-white">
<div>
<Trans i18nKey="cookieConsent">
<a
className="text-white underline hover:text-primary-200"
href={link}
rel="noopener noreferrer"
target="_blank"
return lsValue !== "true"
? createPortal(
<Message className="absolute bottom-0 z-[9999] flex w-screen items-center justify-between rounded-none border-none bg-zinc-900 text-white">
<div>
<Trans i18nKey="cookieConsent">
<a
className="text-white underline hover:text-primary-200"
href={link}
rel="noopener noreferrer"
target="_blank"
>
Datenschutzerklärung
</a>
</Trans>
</div>
<Button
className="ml-2 border-2 border-white !bg-zinc-900 text-white hover:border-white hover:!bg-zinc-600 hover:bg-zinc-700 hover:text-white"
data-cypress="cookie-consent-ok-btn"
onClick={() => setLsValue("true")}
variant="outlined"
>
Datenschutzerklärung
</a>
</Trans>
</div>
<Button
className="ml-2 border-2 border-white !bg-zinc-900 text-white hover:border-white hover:!bg-zinc-600 hover:bg-zinc-700 hover:text-white"
data-cypress="cookie-consent-ok-btn"
onClick={() => setLsValue("true")}
variant="outlined"
>
OK
</Button>
</Message>
) : null;
OK
</Button>
</Message>,
document.body,
)
: null;
};

export default CookieBanner;
2 changes: 1 addition & 1 deletion components/MapGeolocation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function MapGeolocation() {
});

return getIsMobileDevice() ? (
<div className="absolute bottom-28 right-5 z-50 flex items-center rounded p-1 backdrop-blur-sm">
<div className="absolute bottom-28 right-5 z-40 flex items-center rounded p-1 backdrop-blur-sm">
<Button
className="flex h-[34px] !w-[34px] items-center justify-center bg-white !px-0 !py-0 !text-primary-500 hover:bg-white hover:!text-primary-200"
onClick={() => geoloc.setTracking(true)}
Expand Down
2 changes: 1 addition & 1 deletion components/MapLayersMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function MapLayersMenu() {
{({ open }) => {
return (
<>
<PopoverButton className="absolute right-5 top-5 z-50 flex items-center rounded-lg p-1 backdrop-blur-sm sm:w-60">
<PopoverButton className="absolute right-5 top-5 z-30 flex items-center rounded-lg p-1 backdrop-blur-sm sm:w-60">
<div
className={`${buttonStyles} ${outlinedStyles} flex w-full justify-between gap-2 border-none !px-3`}
>
Expand Down
52 changes: 41 additions & 11 deletions components/MapLocation.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useRouter } from "next/router";
import { Map } from "ol";
import OLFeature from "ol/Feature";
import { Point } from "ol/geom";
import { transform } from "ol/proj";
Expand Down Expand Up @@ -123,6 +124,13 @@ const iconFeature: OLFeature = new OLFeature({
geometry: new Point([4756710, -2831367]),
});

function getFeaturesFromLayers(coordinate: Coordinate, map: Map) {
const pixel: number[] = map.getPixelFromCoordinate(coordinate);
const features = (map?.getFeaturesAtPixel(pixel) ??
[]) as unknown as TreeAppGeoJsonFeature[];
return features;
}

function MapLocation() {
const map = useContext(MapContext);
const { maplibreLayer, markerLayer, userLocationsLayer } =
Expand All @@ -135,14 +143,8 @@ function MapLocation() {
const { i18n, t } = useTranslation();

useEffect(() => {
const handleCoords = (
{ coordinate }: { coordinate: Coordinate },
resetFormLocation = true,
) => {
const pixel: number[] = map.getPixelFromCoordinate(coordinate);
const features = (map?.getFeaturesAtPixel(pixel) ??
[]) as unknown as TreeAppGeoJsonFeature[];

const updateForm = (coordinate: Coordinate, resetFormLocation = true) => {
const features = getFeaturesFromLayers(coordinate, map);
if (
features.some(
(f) =>
Expand Down Expand Up @@ -180,11 +182,39 @@ function MapLocation() {
setMapLocation(location, true, true, "f");
}
};

const handleClick = (
{ coordinate }: { coordinate: Coordinate },
resetFormLocation = true,
) => {
const hasFtFeature = getFeaturesFromLayers(coordinate, map).some(
(f) => f.sourceLayer === "forest_types",
);
const currentZoom = map.getView().getZoom();

if (currentZoom && currentZoom <= 13 && hasFtFeature) {
map.getView().animate(
{
center: coordinate,
duration: 500,
zoom: 13,
},
() => {
maplibreLayer?.waitForVectorTileLayerToRender("ft", () =>
updateForm(coordinate, resetFormLocation),
);
},
);
} else {
updateForm(coordinate, resetFormLocation);
}
};
maplibreLayer?.on("loadend" as EventTypes, () => {
mapLocation.coordinate &&
handleCoords({ coordinate: to3857(mapLocation.coordinate) }, false);
if (mapLocation.coordinate) {
updateForm(to3857(mapLocation.coordinate), false);
}
});
map.on("singleclick", handleCoords);
map.on("singleclick", handleClick);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [map, activeProfile]);

Expand Down
15 changes: 13 additions & 2 deletions components/spatial/components/layer/LayersProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import { createContext, useContext, useEffect, useState } from "react";
import { BASELAYER_GRAY } from "@/components/MapBaselayerSwitcher";
import useStore from "@/store";

import style from "../../../../map/style.json";
import mapPosition from "../../../icons/mapPosition.svg";
import userLocation from "../../../icons/userLocation.svg";
import style from "../../style.json";
import { MapContext } from "../Map";

import MaplibreLayer from "./MapLibreLayer";
Expand All @@ -39,7 +39,7 @@ interface Metadata {

interface Paint {
"fill-color"?: DataDrivenPropertyValueSpecification<ColorSpecification>;
"fill-opacity"?: number;
"fill-opacity"?: DataDrivenPropertyValueSpecification<number> | number;
"line-opacity"?: number;
"text-opacity"?: number;
}
Expand Down Expand Up @@ -100,6 +100,17 @@ export const getStyle = (
} else if (layer.type === "symbol") {
paint["text-opacity"] = isSourceLayer ? 1 : 0.0;
}
if (/^ft$/.test(layer.id)) {
paint["fill-opacity"] = [
"interpolate",
["linear"],
["zoom"],
12,
0.8,
22,
0.5,
];
}
if (/^ft_label$/.test(layer.id)) {
// eslint-disable-next-line no-param-reassign
layer.layout["text-field"] = [
Expand Down
44 changes: 23 additions & 21 deletions components/spatial/components/layer/MapLibreLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,7 @@ class MaplibreLayer extends Layer {
});

this.maplibreMap.on("load", () => {
const layers = (
(this.maplibreMap.getStyle().layers || []) as [
TreeAppLayerSpecification,
]
).filter((l) => l.metadata?.group === "main");

const loadedLayers = layers.map((layer) => ({
id: layer.id,
loaded: false,
}));
layers.forEach((layer) => {
this.waitForVectorTileLayerToRender(layer.id, () => {
const styleLayer = loadedLayers.find((l) => l.id === layer.id);
if (styleLayer) {
styleLayer.loaded = true;
}
if (loadedLayers.every((l) => l.loaded)) {
this.dispatchEvent("loadend");
}
});
});
this.getLayersAreLoaded();
});

this.map = map;
Expand All @@ -74,6 +54,28 @@ class MaplibreLayer extends Layer {
this.renderer_ = new MaplibreRenderer({ layer: this });
}

getLayersAreLoaded() {
const layers = (
(this.maplibreMap.getStyle().layers || []) as [TreeAppLayerSpecification]
).filter((l) => l.metadata?.group === "main");

const loadedLayers = layers.map((layer) => ({
id: layer.id,
loaded: false,
}));
layers.forEach((layer) => {
this.waitForVectorTileLayerToRender(layer.id, () => {
const styleLayer = loadedLayers.find((l) => l.id === layer.id);
if (styleLayer) {
styleLayer.loaded = true;
}
if (loadedLayers.every((l) => l.loaded)) {
this.dispatchEvent("loadend");
}
});
});
}

/**
* Waits for a Maplibre vector tile layer to be fully loaded and rendered.
* Calls the provided callback when the layer is ready.
Expand Down
Loading