From 2d8086025466dc877b3a60124aa2ab4822ca00f4 Mon Sep 17 00:00:00 2001 From: williamwoldum Date: Sun, 27 Oct 2024 21:31:38 +0100 Subject: [PATCH] temp --- @types/leaflet-pixi-overlay/index.d.ts | 74 ++++++++++++++++ assets/marker.png | Bin 0 -> 14556 bytes package-lock.json | 75 ++++++++++++++++ package.json | 2 + src/components/map.tsx | 13 +-- src/implementations/MapOverlay.ts | 113 +++++++++++++++++++++++++ src/pages/vesselMapPage.tsx | 4 +- tsconfig.app.json | 4 +- 8 files changed, 271 insertions(+), 14 deletions(-) create mode 100644 @types/leaflet-pixi-overlay/index.d.ts create mode 100644 assets/marker.png create mode 100644 src/implementations/MapOverlay.ts diff --git a/@types/leaflet-pixi-overlay/index.d.ts b/@types/leaflet-pixi-overlay/index.d.ts new file mode 100644 index 0000000..5fcb5eb --- /dev/null +++ b/@types/leaflet-pixi-overlay/index.d.ts @@ -0,0 +1,74 @@ +import 'leaflet' +import * as PIXI from 'pixi.js' + +declare module 'leaflet' { + interface PixiOverlayOptions extends L.LayerOptions { + padding: number + forceCanvas: boolean + doubleBuffering: boolean + resolution: number + projectionZoom: (map: L.Map) => number + destroyInteractionManager: boolean + autoPreventDefault: boolean + preserveDrawingBuffer: boolean + clearBeforeRender: boolean + shouldRedrawOnMove: () => boolean + } + + type LatLngToLayerPointFn = (latLng: L.LatLng, zoom?: number) => L.Point + type LayerPointToLatLngFn = (point: L.Point, zoom?: number) => L.LatLng + + interface PixiOverlayUtils { + latLngToLayerPoint: LatLngToLayerPointFn + layerPointToLatLng: LayerPointToLatLngFn + getScale: (zoom?: number) => number | undefined + getRenderer: () => PIXI.IRenderer + getContainer: () => PIXI.Container + getMap: () => L.Map + } + + type DrawCallbackFn = (utils: PixiOverlayUtils, container: PIXI.Container) => void + + interface LeafletPixiOverlayDefnition extends Omit { + utils: PixiOverlayUtils + options: PixiOverlayOptions + _container?: HTMLElement + _pixiContainer: PIXI.Container + _rendererOptions: Partial + _doubleBuffering: boolean + _map?: L.Map + _renderer: PIXI.IRenderer + _auxRenderer: PIXI.IRenderer + _initialZoom?: number + _wgsOrigin?: L.LatLng + _wgsInitialShift?: L.Point + _mapInitialZoom?: number + _drawCallback: (utils: PixiOverlayUtils, event: L.LeafletEvent) => void + _setMap: (map: L.Map) => void + _setContainerStyle: () => void + _addContainer: () => void + _setEvents: () => void + _onZoom: () => void + _onAnimZoom: (event: L.ZoomAnimEvent) => void + _onMove: (event: L.LeafletEvent) => void + _updateTransform: (center: Pick, zoom: Pick) => void + _redraw: (offset: number, container: PIXI.Container) => void + _update: (event: Partial) => void + _disableLeafletRounding: () => void + _enableLeafletRounding: () => void + initialize: ( + drawCallback: DrawCallbackFn, + pixiContainer: PIXI.Container, + options?: Partial + ) => void + redraw: (data: PIXI.Container) => this + onAdd: (map: L.Map) => void + onRemove: () => void + } + + function pixiOverlay( + drawCallback: DrawCallbackFn, + pixiContainer: PIXI.Container, + options?: Partial + ): LeafletPixiOverlayDefnition +} diff --git a/assets/marker.png b/assets/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..1b8382869a1be2df97c5e9d9ae475eb84f1fd573 GIT binary patch literal 14556 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 8" } }, + "node_modules/@pixi/colord": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz", + "integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA==" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1660,6 +1667,16 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/css-font-loading-module": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.12.tgz", + "integrity": "sha512-x2tZZYkSxXqWvTDgveSynfjq/T2HyiZHXb00j/+gy19yp70PHCizM48XFdjBCWH7eHBD0R5i/pw9yMBP/BH5uA==" + }, + "node_modules/@types/earcut": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz", + "integrity": "sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -1985,6 +2002,19 @@ "vite": "^4.2.0 || ^5.0.0" } }, + "node_modules/@webgpu/types": { + "version": "0.1.49", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.49.tgz", + "integrity": "sha512-NMmS8/DofhH/IFeW+876XrHVWel+J/vdcFCHLDqeJgkH9x0DeiwjVd8LcBdaxdG/T7Rf8VUAYsA8X1efMzLjRQ==" + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -2603,6 +2633,11 @@ "detect-libc": "^1.0.3" } }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2944,6 +2979,11 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3391,6 +3431,11 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/ismobilejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz", + "integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==" + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -3491,6 +3536,15 @@ "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", "license": "BSD-2-Clause" }, + "node_modules/leaflet-pixi-overlay": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet-pixi-overlay/-/leaflet-pixi-overlay-1.9.4.tgz", + "integrity": "sha512-ntSoBULBIlmkLKie2T8/OS6Rh+WpVr+RSeLZUb6DJmO/EhkhwJg6tFUGTQOhJinfixkWF6lc3zpsMd5vg/daWQ==", + "peerDependencies": { + "leaflet": ">=0.7", + "pixi.js": ">=4.6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3781,6 +3835,11 @@ "node": ">=6" } }, + "node_modules/parse-svg-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3876,6 +3935,22 @@ "node": ">= 6" } }, + "node_modules/pixi.js": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.5.2.tgz", + "integrity": "sha512-TOt9g8ifOj4R9DN9ST1M8t2nvnuhr5oWL5YW9ywFLbnOVgFMDcEz+Xek5Mo8Xr64D+QU3qre3IFgreBlsHxTNw==", + "dependencies": { + "@pixi/colord": "^2.9.6", + "@types/css-font-loading-module": "^0.0.12", + "@types/earcut": "^2.1.4", + "@webgpu/types": "^0.1.40", + "@xmldom/xmldom": "^0.8.10", + "earcut": "^2.2.4", + "eventemitter3": "^5.0.1", + "ismobilejs": "^1.1.1", + "parse-svg-path": "^0.1.2" + } + }, "node_modules/polyclip-ts": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/polyclip-ts/-/polyclip-ts-0.16.6.tgz", diff --git a/package.json b/package.json index 0f7d354..e480cc3 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "bootstrap-icons": "^1.11.3", "concurrently": "^9.0.1", "leaflet": "^1.9.4", + "leaflet-pixi-overlay": "^1.9.4", + "pixi.js": "^8.5.2", "react": "^18.3.1", "react-dom": "^18.3.1", "react-leaflet": "^4.2.1", diff --git a/src/components/map.tsx b/src/components/map.tsx index 7d55eb8..ae30f28 100644 --- a/src/components/map.tsx +++ b/src/components/map.tsx @@ -1,6 +1,5 @@ import { MapContainer, TileLayer, useMap } from 'react-leaflet' - -import L from 'leaflet' +import PixiOverlay from '../implementations/MapOverlay' const ComponentResize = () => { const map = useMap() @@ -12,12 +11,7 @@ const ComponentResize = () => { return null } -interface IMap { - children: React.ReactNode - setMapRef: React.Dispatch> -} - -const LMap = ({ setMapRef, children }: IMap) => { +const LMap = () => { return ( { zoom={8} minZoom={3} scrollWheelZoom={true} - ref={setMapRef} > - {children} + ) } diff --git a/src/implementations/MapOverlay.ts b/src/implementations/MapOverlay.ts new file mode 100644 index 0000000..4223895 --- /dev/null +++ b/src/implementations/MapOverlay.ts @@ -0,0 +1,113 @@ +import * as PIXI from 'pixi.js' +import L from 'leaflet' +import { useMap } from 'react-leaflet' +import 'leaflet-pixi-overlay' + +const markerTexture = await PIXI.Assets.load('assets/marker.png') +const markerLatLng = [56.15674, 10.21076] + +interface ICusMarker { + sprite: PIXI.Sprite + popup: L.Popup + currentScale?: number + targetScale?: number +} + +export default function PixiOverlay() { + const mapRef = useMap() + + let frame: number | null = null + let firstDraw = true + let prevZoom: number | null = null + + const sprite = new PIXI.Sprite(markerTexture) + sprite.interactive = true + sprite.cursor = 'pointer' + + const popup = L.popup({ className: 'pixi-popup' }) + .setLatLng(markerLatLng as L.LatLngExpression) + .setContent('Hello world!
I am a popup.') + .openOn(mapRef) + + const cusMarker: ICusMarker = { sprite, popup } + + const pixiContainer = new PIXI.Container() + pixiContainer.addChild(cusMarker.sprite) + + L.pixiOverlay( + (utils) => { + if (frame) { + cancelAnimationFrame(frame) + frame = null + } + const zoom = utils.getMap().getZoom() + const container = utils.getContainer() + const renderer = utils.getRenderer() + const project = utils.latLngToLayerPoint + const scale = utils.getScale() + + if (firstDraw) { + const boundary = new PIXI.EventBoundary(container) + utils.getMap().on('click', (e) => { + const interaction = utils.getRenderer().events + const pointerEvent = e.originalEvent + const pixiPoint = new PIXI.Point() + // get global click position in pixiPoint: + interaction.mapPositionToPoint(pixiPoint, pointerEvent.clientX, pointerEvent.clientY) + // get what is below the click if any: + const target = boundary.hitTest(pixiPoint.x, pixiPoint.y) + if (target && target.uid === cusMarker.sprite.uid) { + cusMarker.popup.openOn(mapRef) + } + }) + + const markerCoords = project(new L.LatLng(markerLatLng[0], markerLatLng[1])) + cusMarker.sprite.x = markerCoords.x + cusMarker.sprite.y = markerCoords.y + cusMarker.sprite.anchor.set(0.5, 1) + cusMarker.sprite.scale.set(1 / scale!) + cusMarker.currentScale = 1 / scale! + } + if (firstDraw || prevZoom !== zoom) { + cusMarker.currentScale = cusMarker.sprite.scale.x + cusMarker.targetScale = 1 / scale! + + // We can draw anything PIXI here. For example, a polygon: + } + + const duration = 100 + let start: number | null = null + + const animate = (timestamp: number) => { + if (start === null) start = timestamp + const progress = timestamp - start + let lambda = progress / duration + if (lambda > 1) lambda = 1 + lambda = lambda * (0.4 + lambda * (2.2 + lambda * -1.6)) + cusMarker.sprite.scale.set( + cusMarker.currentScale! + lambda * (cusMarker.targetScale! - cusMarker.currentScale!) + ) + renderer.render(container) + if (progress < duration) { + frame = requestAnimationFrame(animate) + } + } + + if (!firstDraw && prevZoom !== zoom) { + start = null + frame = requestAnimationFrame(animate) + } + + firstDraw = false + prevZoom = zoom + renderer.render(container) + }, + pixiContainer, + { + doubleBuffering: true, + autoPreventDefault: false, + } + ).addTo(mapRef) + + return null +} diff --git a/src/pages/vesselMapPage.tsx b/src/pages/vesselMapPage.tsx index 29f774e..c3eb32e 100644 --- a/src/pages/vesselMapPage.tsx +++ b/src/pages/vesselMapPage.tsx @@ -81,9 +81,7 @@ export default function VesselMapPage() {
- - {allVessels && allVessels.length > 0 ? : <>} - +
{/*
diff --git a/tsconfig.app.json b/tsconfig.app.json index f0a2350..83a0a4e 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -5,6 +5,7 @@ "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, + "typeRoots": ["./@types", "./node_modules/@types"], /* Bundler mode */ "moduleResolution": "bundler", @@ -20,5 +21,6 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["src"] + "include": ["src", "./@types"], + "exclude": ["./node_modules"] }