diff --git a/package.json b/package.json index 04c1a14..74d53a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lageplan", - "version": "1.0.2", + "version": "1.0.3", "description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation", "private": true, "scripts": { diff --git a/src/app/app/page.tsx b/src/app/app/page.tsx index 79bf53f..f103fd5 100644 --- a/src/app/app/page.tsx +++ b/src/app/app/page.tsx @@ -1130,6 +1130,32 @@ export default function AppPage() { } } + // Draw arrowheads for arrow features + for (const f of currentFeatures.filter(f => f.type === 'arrow')) { + if (f.geometry.type !== 'LineString') continue + const lineCoords = f.geometry.coordinates as number[][] + if (lineCoords.length < 2) continue + const p1 = lineCoords[lineCoords.length - 2] + const p2 = lineCoords[lineCoords.length - 1] + const px1 = mapInstance.project(p1 as [number, number]) + const px2 = mapInstance.project(p2 as [number, number]) + const angle = Math.atan2(px2.y - px1.y, px2.x - px1.x) + const color = (f.properties.color as string) || '#000000' + const arrowSize = 14 * dpr + + ctx.save() + ctx.translate(px2.x * dpr, px2.y * dpr) + ctx.rotate(angle + Math.PI / 2) + ctx.beginPath() + ctx.moveTo(0, -arrowSize) + ctx.lineTo(-arrowSize * 0.7, arrowSize * 0.3) + ctx.lineTo(arrowSize * 0.7, arrowSize * 0.3) + ctx.closePath() + ctx.fillStyle = color + ctx.fill() + ctx.restore() + } + // Draw line/polygon label markers at midpoints for (const f of currentFeatures.filter(f => f.properties.label && (f.geometry.type === 'LineString' || f.geometry.type === 'Polygon'))) { const label = f.properties.label as string diff --git a/src/components/journal/journal-view.tsx b/src/components/journal/journal-view.tsx index a21c84e..4d09b6a 100644 --- a/src/components/journal/journal-view.tsx +++ b/src/components/journal/journal-view.tsx @@ -1030,8 +1030,8 @@ export function JournalView({ projectId, projectTitle, projectLocation, einsatzl if (mapRef?.current) { const canvas = mapRef.current.getCanvas() if (canvas) { - // Resize to max 1600px wide and convert to JPEG - const maxW = 1600 + // Resize to max 2400px wide and convert to JPEG + const maxW = 2400 const ratio = Math.min(1, maxW / canvas.width) const offscreen = document.createElement('canvas') offscreen.width = Math.round(canvas.width * ratio) @@ -1039,18 +1039,18 @@ export function JournalView({ projectId, projectTitle, projectLocation, einsatzl const ctx = offscreen.getContext('2d') if (ctx) { ctx.drawImage(canvas, 0, 0, offscreen.width, offscreen.height) - mapScreenshot = offscreen.toDataURL('image/jpeg', 0.75) + mapScreenshot = offscreen.toDataURL('image/jpeg', 0.85) } } } } catch (e) { console.warn('Map screenshot failed:', e) } - } else if (rawScreenshot.length > 500000) { + } else if (rawScreenshot.length > 800000) { // Compress pre-captured screenshot if too large try { const img = new Image() img.src = rawScreenshot await new Promise(r => { img.onload = r; img.onerror = r }) - const maxW = 1600 + const maxW = 2400 const ratio = Math.min(1, maxW / img.naturalWidth) const offscreen = document.createElement('canvas') offscreen.width = Math.round(img.naturalWidth * ratio) @@ -1058,7 +1058,7 @@ export function JournalView({ projectId, projectTitle, projectLocation, einsatzl const ctx = offscreen.getContext('2d') if (ctx) { ctx.drawImage(img, 0, 0, offscreen.width, offscreen.height) - mapScreenshot = offscreen.toDataURL('image/jpeg', 0.75) + mapScreenshot = offscreen.toDataURL('image/jpeg', 0.85) } } catch { mapScreenshot = rawScreenshot } } else { diff --git a/src/components/map/map-view.tsx b/src/components/map/map-view.tsx index de54ce8..d1988bc 100644 --- a/src/components/map/map-view.tsx +++ b/src/components/map/map-view.tsx @@ -1377,12 +1377,12 @@ export function MapView({ const lineCoords = f.geometry.coordinates as number[][] if (lineCoords.length < 2) return - // Get last two points to calculate arrow direction + // Get last two points to calculate arrow direction using screen-projected coords const p1 = lineCoords[lineCoords.length - 2] const p2 = lineCoords[lineCoords.length - 1] - const angle = Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * (180 / Math.PI) - // MapLibre uses screen coords where Y is inverted, so negate the angle - const screenAngle = -angle + 90 + const px1 = map.current.project(p1 as [number, number]) + const px2 = map.current.project(p2 as [number, number]) + const screenAngle = Math.atan2(px2.y - px1.y, px2.x - px1.x) * (180 / Math.PI) + 90 const color = (f.properties.color as string) || '#000000' const arrowEl = document.createElement('div') diff --git a/src/lib/rapport-pdf.tsx b/src/lib/rapport-pdf.tsx index fd3bd26..0a6d069 100644 --- a/src/lib/rapport-pdf.tsx +++ b/src/lib/rapport-pdf.tsx @@ -4,7 +4,7 @@ import { Document, Page, Text, View, StyleSheet, Font, Image } from '@react-pdf/ // Register default font (Helvetica is built-in) const styles = StyleSheet.create({ page: { - padding: '12mm 15mm 15mm', + padding: '12mm 15mm 32mm', fontFamily: 'Helvetica', fontSize: 10, lineHeight: 1.4,