nestjs rewrite
This commit is contained in:
234
client/src/components/map/MapPrint/MapPrint.tsx
Normal file
234
client/src/components/map/MapPrint/MapPrint.tsx
Normal file
@ -0,0 +1,234 @@
|
||||
import { ActionIcon, Button, Checkbox, Flex, Modal, Radio, ScrollAreaAutosize, Select, Stack, Text } from '@mantine/core'
|
||||
import { IconHelp, IconWindowMaximize, IconWindowMinimize } from '@tabler/icons-react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { clearPrintArea, PrintScale, setPreviousView, setPrintScale, setPrintScaleLine, useMapStore } from '../../../store/map'
|
||||
import { PrintFormat, PrintOrientation, printResolutions, setPrintOrientation, setPrintResolution, usePrintStore } from '../../../store/print'
|
||||
import { printDimensions, scaleOptions } from '../../../constants/map'
|
||||
import { useObjectsStore } from '../../../store/objects'
|
||||
// import { getPointResolution } from 'ol/proj'
|
||||
import jsPDF from 'jspdf'
|
||||
import { getCenter } from 'ol/extent'
|
||||
import ScaleLine from 'ol/control/ScaleLine'
|
||||
|
||||
const MapPrint = ({
|
||||
id,
|
||||
mapElement
|
||||
}: {
|
||||
id: string
|
||||
mapElement: React.MutableRefObject<HTMLDivElement | null>
|
||||
}) => {
|
||||
const [fullscreen, setFullscreen] = useState(false)
|
||||
const { printOrientation, printResolution, printFormat } = usePrintStore()
|
||||
const { selectedYear, selectedRegion, selectedDistrict } = useObjectsStore().id[id]
|
||||
|
||||
const {
|
||||
map,
|
||||
previousView, printArea, printSource, printAreaDraw, printScale, printScaleLine,
|
||||
} = useMapStore().id[id]
|
||||
|
||||
const exportToPDF = (format: PrintFormat, resolution: number, orientation: PrintOrientation) => {
|
||||
const dim = printDimensions[format]
|
||||
|
||||
const width = Math.round((dim[orientation === 'horizontal' ? 0 : 1] * resolution) / 25.4)
|
||||
const height = Math.round((dim[orientation === 'horizontal' ? 1 : 0] * resolution) / 25.4)
|
||||
|
||||
if (!map) return
|
||||
|
||||
// Store original size and scale
|
||||
const originalSize = map.getSize()
|
||||
const originalResolution = map.getView().getResolution()
|
||||
|
||||
if (!originalSize || !originalResolution) return
|
||||
|
||||
// Calculate new resolution to fit high DPI
|
||||
const scaleFactor = width / originalSize[0]
|
||||
const newResolution = originalResolution / scaleFactor
|
||||
|
||||
// console.log(`New resolution: ${newResolution}`)
|
||||
|
||||
// const center = map.getView().getCenter()
|
||||
// let scaleResolution = 1
|
||||
// if (center) {
|
||||
// scaleResolution = Number(printScale) / getPointResolution(map.getView().getProjection(), Number(resolution) / 25.4, center)
|
||||
// // console.log(`Scaled resolution: ${scaleResolution}`)
|
||||
// }
|
||||
|
||||
console.log(width, height)
|
||||
// Set new high-resolution rendering
|
||||
map.setSize([width, height])
|
||||
map.getView().setResolution(newResolution)
|
||||
map.renderSync()
|
||||
|
||||
map.once("rendercomplete", function () {
|
||||
const mapCanvas = document.createElement("canvas")
|
||||
mapCanvas.width = width
|
||||
mapCanvas.height = height
|
||||
const mapContext = mapCanvas.getContext("2d")
|
||||
|
||||
if (!mapContext) return
|
||||
|
||||
const canvas = document.querySelector('canvas')
|
||||
if (canvas) {
|
||||
if (canvas.width > 0) {
|
||||
const opacity = canvas.parentElement?.style.opacity || "1"
|
||||
mapContext.globalAlpha = parseFloat(opacity)
|
||||
|
||||
const transform = canvas.style.transform
|
||||
const matrixMatch = transform.match(/^matrix\(([^)]+)\)$/)
|
||||
if (matrixMatch) {
|
||||
const matrix = matrixMatch[1].split(",").map(Number)
|
||||
mapContext.setTransform(...matrix as [number, number, number, number, number, number])
|
||||
}
|
||||
|
||||
mapContext.drawImage(canvas, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
mapContext.globalAlpha = 1
|
||||
mapContext.setTransform(1, 0, 0, 1, 0, 0)
|
||||
|
||||
// Restore original map settings
|
||||
map.setSize(originalSize)
|
||||
map.getView().setResolution(originalResolution)
|
||||
map.renderSync()
|
||||
|
||||
const dimensions = {
|
||||
w: orientation === 'horizontal' ? dim[0] : dim[1],
|
||||
h: orientation === 'horizontal' ? dim[1] : dim[0]
|
||||
}
|
||||
|
||||
// Generate PDF
|
||||
const pdf = new jsPDF(orientation === 'horizontal' ? "landscape" : 'portrait', undefined, format)
|
||||
pdf.addImage(mapCanvas.toDataURL("image/jpeg"), "JPEG", 0, 0, dimensions.w, dimensions.h)
|
||||
|
||||
const filename = `${selectedYear}-${selectedRegion}-${selectedDistrict}-${new Date().toISOString()}.pdf`
|
||||
pdf.save(filename, {
|
||||
returnPromise: true
|
||||
}).then(() => {
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const scaleLine = useRef(new ScaleLine({
|
||||
bar: true,
|
||||
text: true,
|
||||
minWidth: 125
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
if (printArea) {
|
||||
// backup view before entering print mode
|
||||
setPreviousView(id, map?.getView())
|
||||
|
||||
map?.setTarget('print-portal')
|
||||
|
||||
printSource.clear()
|
||||
map?.getView().setCenter(getCenter(printArea))
|
||||
map?.getView().fit(printArea, {
|
||||
size: printOrientation === 'horizontal' ? [594, 420] : [420, 594]
|
||||
})
|
||||
map?.removeInteraction(printAreaDraw)
|
||||
}
|
||||
}, [printArea, map])
|
||||
|
||||
useEffect(() => {
|
||||
if (printScaleLine && printArea) {
|
||||
map?.addControl(scaleLine.current)
|
||||
} else {
|
||||
map?.removeControl(scaleLine.current)
|
||||
}
|
||||
}, [printScaleLine, printArea])
|
||||
|
||||
return (
|
||||
<Modal.Root
|
||||
scrollAreaComponent={ScrollAreaAutosize}
|
||||
keepMounted size='auto'
|
||||
opened={!!printArea}
|
||||
onClose={() => {
|
||||
clearPrintArea(id)
|
||||
map?.setTarget(mapElement.current as HTMLDivElement)
|
||||
map?.addInteraction(printAreaDraw)
|
||||
}} fullScreen={fullscreen}>
|
||||
<Modal.Overlay />
|
||||
<Modal.Content style={{ transition: 'all .3s ease' }}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>
|
||||
Предпросмотр области печати
|
||||
</Modal.Title>
|
||||
|
||||
<Flex ml='auto' gap='md'>
|
||||
<ActionIcon title='Помощь' ml='auto' variant='transparent'>
|
||||
<IconHelp color='gray' />
|
||||
</ActionIcon>
|
||||
<ActionIcon title={fullscreen ? 'Свернуть' : 'Развернуть'} variant='transparent' onClick={() => setFullscreen(!fullscreen)}>
|
||||
{fullscreen ? <IconWindowMinimize color='gray' /> : <IconWindowMaximize color='gray' />}
|
||||
</ActionIcon>
|
||||
<Modal.CloseButton title='Закрыть' />
|
||||
</Flex>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Stack align='center'>
|
||||
<Text w='100%'>Область печати можно передвигать.</Text>
|
||||
|
||||
<div id='print-portal' style={{
|
||||
width: printOrientation === 'horizontal' ? '594px' : '420px',
|
||||
height: printOrientation === 'horizontal' ? '420px' : '594px'
|
||||
}}>
|
||||
|
||||
</div>
|
||||
|
||||
<Flex w='100%' wrap='wrap' gap='lg' justify='space-between'>
|
||||
<Radio.Group
|
||||
label='Ориентация'
|
||||
value={printOrientation}
|
||||
onChange={(value) => setPrintOrientation(value as PrintOrientation)}
|
||||
>
|
||||
<Stack>
|
||||
<Radio value='horizontal' label='Горизонтальная' />
|
||||
<Radio value='vertical' label='Вертикальная' />
|
||||
</Stack>
|
||||
</Radio.Group>
|
||||
|
||||
<Select
|
||||
allowDeselect={false}
|
||||
label="Разрешение"
|
||||
placeholder="Выберите разрешение"
|
||||
data={printResolutions}
|
||||
value={printResolution.toString()}
|
||||
onChange={(value) => setPrintResolution(Number(value))}
|
||||
/>
|
||||
|
||||
<Select
|
||||
allowDeselect={false}
|
||||
label="Масштаб"
|
||||
placeholder="Выберите масштаб"
|
||||
data={scaleOptions}
|
||||
value={printScale}
|
||||
onChange={(value) => setPrintScale(id, value as PrintScale)}
|
||||
/>
|
||||
|
||||
<Checkbox
|
||||
checked={printScaleLine}
|
||||
label="Масштабная линия"
|
||||
onChange={(event) => setPrintScaleLine(id, event.currentTarget.checked)}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex w='100%' gap='sm' align='center'>
|
||||
<Button ml='auto' onClick={() => {
|
||||
if (previousView) {
|
||||
exportToPDF(printFormat, printResolution, printOrientation)
|
||||
}
|
||||
}}>
|
||||
Печать
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Modal.Body>
|
||||
</Modal.Content>
|
||||
</Modal.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export default MapPrint
|
Reference in New Issue
Block a user