From ada3b63b8dc4e45b9810a20b2b8dbd5f067dab36 Mon Sep 17 00:00:00 2001 From: popovspiridon99 Date: Fri, 7 Mar 2025 16:50:54 +0900 Subject: [PATCH] pass aspect ratio to fixedAspectRatio; remove printAreaDraw after printArea is defined --- client/src/components/map/MapComponent.tsx | 404 ++++++++++++--------- client/src/components/map/MapMode.tsx | 130 +++++-- client/src/components/map/mapUtils.ts | 9 +- client/src/constants/map.ts | 48 +++ client/src/store/map.ts | 72 +++- client/src/store/print.ts | 23 ++ 6 files changed, 485 insertions(+), 201 deletions(-) create mode 100644 client/src/constants/map.ts create mode 100644 client/src/store/print.ts diff --git a/client/src/components/map/MapComponent.tsx b/client/src/components/map/MapComponent.tsx index 3c9d7fc..356e255 100644 --- a/client/src/components/map/MapComponent.tsx +++ b/client/src/components/map/MapComponent.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef, useState } from 'react' +import { jsPDF } from "jspdf" import 'ol/ol.css' import { Modify } from 'ol/interaction' import { ImageStatic, Vector as VectorSource } from 'ol/source' @@ -14,13 +15,13 @@ import { addInteractions, handleImageDrop, loadFeatures, processFigure, processL import useSWR, { SWRConfiguration } from 'swr' import { fetcher } from '../../http/axiosInstance' import { BASE_URL } from '../../constants' -import { ActionIcon, Autocomplete, CloseButton, Flex, Select as MantineSelect, MantineStyleProp, rem, useMantineColorScheme, Portal, Menu, Button, Group, Divider, LoadingOverlay, Stack, Container, Modal, Transition } from '@mantine/core' -import { IconBoxMultiple, IconBoxPadding, IconChevronDown, IconPlus, IconSearch, IconUpload } from '@tabler/icons-react' +import { ActionIcon, Autocomplete, CloseButton, Flex, Select as MantineSelect, MantineStyleProp, rem, useMantineColorScheme, Portal, Menu, Button, Group, Divider, LoadingOverlay, Stack, Container, Modal, Transition, Text, Select, Switch, Checkbox, Radio, ScrollAreaAutosize } from '@mantine/core' +import { IconArrowsMaximize, IconArrowsMinimize, IconBoxMultiple, IconBoxPadding, IconChevronCompactLeft, IconChevronDown, IconChevronLeft, IconChevronsDown, IconHelp, IconPlus, IconSearch, IconUpload, IconWindowMaximize, IconWindowMinimize } from '@tabler/icons-react' import { ICitySettings, IFigure, ILine } from '../../interfaces/gis' import axios from 'axios' import MapToolbar from './MapToolbar/MapToolbar' import MapStatusbar from './MapStatusbar/MapStatusbar' -import { setAlignMode, setSatMapsProvider, setTypeRoles, useMapStore, setMapLabel, clearPrintArea } from '../../store/map' +import { setAlignMode, setSatMapsProvider, setTypeRoles, useMapStore, setMapLabel, clearPrintArea, setPreviousView, setMode, setPrintScale, PrintScale, setPrintScaleLine } from '../../store/map' import { useThrottle } from '@uidotdev/usehooks' import ObjectTree from '../Tree/ObjectTree' import { setCurrentObjectId, setSelectedDistrict, setSelectedRegion, setSelectedYear, useObjectsStore } from '../../store/objects' @@ -32,13 +33,11 @@ import GeoJSON from 'ol/format/GeoJSON' import MapLegend from './MapLegend/MapLegend' import GisService from '../../services/GisService' import MapMode from './MapMode' - -const satMapsProviders = [ - { label: 'Google', value: 'google' }, - { label: 'Яндекс', value: 'yandex' }, - { label: 'Подложка', value: 'custom' }, - { label: 'Static', value: 'static' } -] +import ScaleLine from 'ol/control/ScaleLine' +import { getPointResolution } from 'ol/proj' +import { PrintFormat, PrintOrientation, printResolutions, setPrintOrientation, setPrintResolution, usePrintStore } from '../../store/print' +import { printDimensions, satMapsProviders, scaleOptions, schemas } from '../../constants/map' +import { modals } from "@mantine/modals"; const swrOptions: SWRConfiguration = { revalidateOnFocus: false @@ -73,9 +72,11 @@ const MapComponent = ({ nodeLayerSource, drawingLayerSource, satLayer, staticMapLayer, figuresLayer, linesLayer, regionsLayer, districtBoundLayer, baseLayer, - printArea, printSource, printAreaDraw + previousView, printArea, printSource, printAreaDraw, printScale, printScaleLine, } = useMapStore().id[id] + const { printOrientation, printResolution, printFormat } = usePrintStore() + // Tab settings const objectsPane: ITabsPane[] = [{ title: 'Объекты', value: 'objects', view: }, { title: 'Неразмещенные', value: 'unplaced', view: <> }, { title: 'Другие', value: 'other', view: <> },] const paramsPane: ITabsPane[] = [{ title: 'История изменений', value: 'history', view: <> }, { title: 'Параметры', value: 'parameters', view: }, { title: 'Вычисляемые', value: 'calculated', view: <> }] @@ -180,7 +181,6 @@ const MapComponent = ({ zIndex: '1', backgroundColor: colorScheme === 'light' ? '#F0F0F0CC' : '#000000CC', backdropFilter: 'blur(8px)', - border: '1px solid #00000022' } const { data: nodes } = useSWR('/nodes/all', () => fetcher('/nodes/all', BASE_URL.ems), { revalidateOnFocus: false }) @@ -205,18 +205,18 @@ const MapComponent = ({ }, [nodes, nodeLayerSource]) const [searchObject, setSearchObject] = useState("") - const throttledSearchObject = useThrottle(searchObject, 500); + const throttledSearchObject = useThrottle(searchObject, 500) useEffect(() => { - if (!selectedObjectType || !map) return; + if (!selectedObjectType || !map) return if (figuresLayer) { // Reset styles and apply highlight to matching features figuresLayer.getSource()?.getFeatures().forEach((feature) => { if (selectedObjectType == feature.get('type')) { - feature.setStyle(highlightStyleYellow); + feature.setStyle(highlightStyleYellow) } else { - feature.setStyle(undefined); // Reset to default style + feature.setStyle(undefined) // Reset to default style } }) } @@ -225,9 +225,9 @@ const MapComponent = ({ // Reset styles and apply highlight to matching features linesLayer.getSource()?.getFeatures().forEach((feature) => { if (selectedObjectType == feature.get('type')) { - feature.setStyle(highlightStyleYellow); + feature.setStyle(highlightStyleYellow) } else { - feature.setStyle(undefined); // Reset to default style + feature.setStyle(undefined) // Reset to default style } }) } @@ -248,7 +248,7 @@ const MapComponent = ({ } } else { - feature.setStyle(undefined); // Reset to default style + feature.setStyle(undefined) // Reset to default style } }) } @@ -257,14 +257,14 @@ const MapComponent = ({ // Reset styles and apply highlight to matching features linesLayer.getSource()?.getFeatures().forEach((feature: Feature) => { if (currentObjectId == feature.get('object_id')) { - feature.setStyle(highlightStyleRed); + feature.setStyle(highlightStyleRed) const geometry = feature.getGeometry() if (geometry) { map?.getView().fit(geometry as SimpleGeometry, { duration: 500, maxZoom: 18 }) } } else { - feature.setStyle(undefined); // Reset to default style + feature.setStyle(undefined) // Reset to default style } }) } @@ -311,79 +311,6 @@ const MapComponent = ({ } }, [districtsData, id, selectedDistrict, selectedYear]) - //const [searchParams, setSearchParams] = useSearchParams() - - // useEffect(() => { - // if (selectedRegion) { - // setSearchParams((params) => { - // params.set('r', selectedRegion.toString()); - // return params - // }) - // } - - // if (selectedDistrict) { - // setSearchParams((params) => { - // params.set('d', selectedDistrict?.toString()); - // return params - // }) - // } - - // if (selectedYear) { - // setSearchParams((params) => { - // params.set('y', selectedYear?.toString()); - // return params - // }) - // } - - // if (currentObjectId) { - // setSearchParams((params) => { - // params.set('o', currentObjectId?.toString()); - // return params - // }) - // } - // }, [selectedRegion, selectedDistrict, selectedYear, currentObjectId, setSearchParams]) - - // useEffect(() => { - // if (Array.isArray(regionsData)) { - // const region = searchParams.get('r') - - // if (searchParams.get('r')) { - // setSelectedRegion(Number(region)) - // } - // } - // }, [searchParams, regionsData]) - - // useEffect(() => { - // if (Array.isArray(regionsData)) { - // const district = searchParams.get('d') - - // if (Array.isArray(districtsData)) { - // if (district) { - // setSelectedDistrict(Number(district)) - // } - // } - // } - // }, [searchParams, regionsData, districtsData]) - - - // useEffect(() => { - // if (Array.isArray(regionsData)) { - // const year = searchParams.get('y') - - // if (year) { - // setSelectedYear(Number(year)) - // } - // } - // }, [searchParams, regionsData]) - - // useEffect(() => { - // const object = searchParams.get('o') - - // if (figuresData && linesData && object) { - // setCurrentObjectId(object) - // } - // }, [searchParams, figuresData, linesData]) - useEffect(() => { if (selectedDistrict === null) { setSelectedYear(id, null) @@ -395,6 +322,8 @@ const MapComponent = ({ } }, [selectedDistrict, selectedRegion, id]) + const [leftPaneHidden, setLeftPaneHidden] = useState(false) + useEffect(() => { const districtBoundSource = districtBoundLayer.getSource() @@ -456,9 +385,9 @@ const MapComponent = ({ if (selectedDistrict && districtData) { const settings = getCitySettings() - const imageUrl = `${import.meta.env.VITE_API_EMS_URL}/static/${selectedDistrict}`; - const img = new Image(); - img.src = imageUrl; + const imageUrl = `${import.meta.env.VITE_API_EMS_URL}/static/${selectedDistrict}` + const img = new Image() + img.src = imageUrl img.onload = () => { if (map) { const width = img.naturalWidth @@ -469,25 +398,25 @@ const MapComponent = ({ const wk = width * k const hk = height * k - const center = [settings.offset_x + (wk), settings.offset_y - (hk)]; + const center = [settings.offset_x + (wk), settings.offset_y - (hk)] const extent = [ center[0] - (wk), center[1] - (hk), center[0] + (wk), center[1] + (hk), - ]; + ] // Set up the initial image layer with the extent const imageSource = new ImageStatic({ url: imageUrl, imageExtent: extent, - }); - staticMapLayer.setSource(imageSource); + }) + staticMapLayer.setSource(imageSource) - //map.current.addLayer(imageLayer.current); + //map.current.addLayer(imageLayer.current) } - }; + } } }, [selectedDistrict, districtData, staticMapLayer]) @@ -496,27 +425,33 @@ const MapComponent = ({ baseLayer.on('prerender', function (e) { if (colorScheme === 'dark') { if (e.context) { - const context = e.context as CanvasRenderingContext2D; - context.filter = 'grayscale(80%) invert(100%) hue-rotate(180deg) '; - context.globalCompositeOperation = 'source-over'; + const context = e.context as CanvasRenderingContext2D + context.filter = 'grayscale(80%) invert(100%) hue-rotate(180deg) ' + context.globalCompositeOperation = 'source-over' } } else { if (e.context) { - const context = e.context as CanvasRenderingContext2D; - context.filter = 'none'; + const context = e.context as CanvasRenderingContext2D + context.filter = 'none' } } }) baseLayer.on('postrender', function (e) { if (e.context) { - const context = e.context as CanvasRenderingContext2D; - context.filter = 'none'; + const context = e.context as CanvasRenderingContext2D + context.filter = 'none' } }) } }, [colorScheme]) + const scaleLine = useRef(new ScaleLine({ + bar: true, + text: true, + minWidth: 125 + })) + useEffect(() => { if (map) { if (mode === 'print') { @@ -525,41 +460,199 @@ const MapComponent = ({ map.removeInteraction(printAreaDraw) } } - }, [mode, map, printAreaDraw]) useEffect(() => { if (printArea) { + // backup view before entering print mode + setPreviousView(id, map?.getView()) + map?.setTarget('print-portal') - // map?.setView(new View({ - // extent: printArea - // })) + printSource.clear() map?.getView().setCenter(getCenter(printArea)) map?.getView().fit(printArea, { - size: [640, 320] + size: printOrientation === 'horizontal' ? [594, 420] : [420, 594] }) + map?.removeInteraction(printAreaDraw) } }, [printArea, map]) + 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 + 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() + + // Generate PDF + const pdf = new jsPDF(orientation === 'horizontal' ? "landscape" : 'portrait', undefined, format) + pdf.addImage(mapCanvas.toDataURL("image/jpeg"), "JPEG", 0, 0, orientation === 'horizontal' ? dim[0] : dim[1], orientation === 'horizontal' ? dim[1] : dim[0]) + + const filename = `${selectedYear}-${selectedRegion}-${selectedDistrict}-${new Date().toISOString()}.pdf` + pdf.save(filename, { + returnPromise: true + }).then(() => { + + }) + }) + } + + useEffect(() => { + if (printScaleLine) { + map?.addControl(scaleLine.current) + } else { + map?.removeControl(scaleLine.current) + } + }, [printScaleLine]) + + const [fullscreen, setFullscreen] = useState(false) + return ( <> - { + { clearPrintArea(id) map?.setTarget(mapElement.current as HTMLDivElement) - }} title="Предпросмотр области"> - - - - - + + + + + setFullscreen(!fullscreen)}> + {fullscreen ? : } + + + + + + + Область печати можно передвигать. - - + + + + setPrintOrientation(value as PrintOrientation)} + > + + + + + + + setPrintScale(id, value as PrintScale)} + /> + + setPrintScaleLine(id, event.currentTarget.checked)} + /> + + + + + + + + + + {active && @@ -594,13 +687,7 @@ const MapComponent = ({ data={regionsData ? regionsData.map((item: { name: string, id: number }) => ({ label: item.name, value: item.id.toString() })) : []} onChange={(value) => setSelectedRegion(id, Number(value))} clearable - onClear={() => { - setSelectedRegion(id, null) - // setSearchParams((params) => { - // params.delete('r') - // return params - // }) - }} + onClear={() => setSelectedRegion(id, null)} searchable value={selectedRegion ? selectedRegion.toString() : null} /> @@ -611,19 +698,13 @@ const MapComponent = ({ data={districtsData ? districtsData.map((item: { name: string, id: number, district_name: string }) => ({ label: [item.name, item.district_name].join(' - '), value: item.id.toString() })) : []} onChange={(value) => setSelectedDistrict(id, Number(value))} clearable - onClear={() => { - setSelectedDistrict(id, null) - // setSearchParams((params) => { - // params.delete('d') - // return params - // }) - }} + onClear={() => { setSelectedDistrict(id, null) }} searchable value={selectedDistrict ? selectedDistrict.toString() : null} /> ({ label: el, value: el }))} + data={schemas.map(el => ({ label: el, value: el }))} onChange={(e) => { if (e) { setSelectedYear(id, Number(e)) @@ -631,13 +712,7 @@ const MapComponent = ({ setSelectedYear(id, null) } }} - onClear={() => { - setSelectedYear(id, null) - // setSearchParams((params) => { - // params.delete('y') - // return params - // }) - }} + onClear={() => setSelectedYear(id, null)} value={selectedYear ? selectedYear?.toString() : null} clearable /> @@ -682,19 +757,22 @@ const MapComponent = ({ - - - {(styles) => - - - - } - + + + {selectedRegion && selectedDistrict && selectedYear && + + + + + + } + + {!!selectedRegion && !!selectedDistrict && !!selectedYear && + + } + @@ -735,6 +813,6 @@ const MapComponent = ({ ) -}; +} export default MapComponent diff --git a/client/src/components/map/MapMode.tsx b/client/src/components/map/MapMode.tsx index 6d8fc4a..2bfd9ca 100644 --- a/client/src/components/map/MapMode.tsx +++ b/client/src/components/map/MapMode.tsx @@ -1,40 +1,106 @@ -import { Center, SegmentedControl } from '@mantine/core' -import { getMode, Mode, setMode } from '../../store/map' -import { IconEdit, IconEye, IconPrinter } from '@tabler/icons-react' +import { Button, Flex, FloatingIndicator, Popover, SegmentedControl } from '@mantine/core' +import { Mode, setMode, useMapStore } from '../../store/map' +import { IconChevronDown, IconCropLandscape, IconCropPortrait, IconEdit, IconEye, IconPrinter } from '@tabler/icons-react' +import { useEffect, useState } from 'react' +import { PrintOrientation, setPrintOrientation, usePrintStore } from '../../store/print' const MapMode = ({ map_id }: { map_id: string }) => { + const [rootRef, setRootRef] = useState(null); + const [controlsRefs, setControlsRefs] = useState>({}); + + const { mode } = useMapStore().id[map_id] + + const setControlRef = (item: Mode) => (node: HTMLButtonElement) => { + controlsRefs[item] = node; + setControlsRefs(controlsRefs); + } + + const { printOrientation } = usePrintStore() + + useEffect(() => { + + }, [printOrientation]) + return ( - setMode(map_id, value as Mode)} color="blue" w='auto' data={[ - { - value: 'view', - label: ( -
- - Просмотр -
- ), - }, - { - value: 'edit', - label: ( -
- - Редактирование -
- ), - }, - { - value: 'print', - label: ( -
- - Печать -
- ), - }, - ]} /> + + + + + + + + + + + + + + + { + setPrintOrientation(value as PrintOrientation) + setMode(map_id, 'print' as Mode) + }} + data={[ + { + value: 'horizontal', + label: ( + + ), + }, + { + value: 'vertical', + label: ( + + ), + }, + ]} + /> + + + + + ) } diff --git a/client/src/components/map/mapUtils.ts b/client/src/components/map/mapUtils.ts index 44ea39c..7efed33 100644 --- a/client/src/components/map/mapUtils.ts +++ b/client/src/components/map/mapUtils.ts @@ -15,9 +15,9 @@ import { ImageStatic } from "ol/source"; import { IFigure, ILine } from "../../interfaces/gis"; import { fromCircle, fromExtent } from "ol/geom/Polygon"; import { measureStyleFunction, modifyStyle } from "./Measure/MeasureStyles"; -import { getCurrentTool, getDraw, getDrawingLayerSource, getImageLayer, getMap, getMeasureClearPrevious, getMeasureDraw, getMeasureModify, getMeasureSource, getMeasureType, getOverlayLayerSource, getSnap, getTipPoint, getTranslate, setDraw, setFile, setMeasureDraw, setPolygonExtent, setRectCoords, setSnap, setTranslate } from "../../store/map"; +import { getCurrentTool, getDraw, getDrawingLayerSource, getImageLayer, getMap, getMeasureClearPrevious, getMeasureDraw, getMeasureModify, getMeasureSource, getMeasureType, getOverlayLayerSource, getSnap, getTipPoint, getTranslate, PrintOrientation, setDraw, setFile, setMeasureDraw, setPolygonExtent, setRectCoords, setSnap, setTranslate } from "../../store/map"; import Collection from "ol/Collection"; -import { GeometryFunction, SketchCoordType } from "ol/interaction/Draw"; +import { SketchCoordType } from "ol/interaction/Draw"; const calculateAngle = (coords: [number, number][]) => { const [start, end] = coords; @@ -342,9 +342,10 @@ export const updateImageSource = ( } }; -export const fixedAspectRatioBox: GeometryFunction = ( +export const fixedAspectRatioBox = ( coordinates: SketchCoordType, geometry: SimpleGeometry | undefined, + orientation?: PrintOrientation ): SimpleGeometry => { if (!Array.isArray(coordinates) || coordinates.length < 2) { return geometry ?? new Polygon([]); @@ -356,7 +357,7 @@ export const fixedAspectRatioBox: GeometryFunction = ( const maxX = end[0]; const width = maxX - minX; - const height = Math.abs(width / 2) + const height = orientation === 'horizontal' ? Math.abs(width / Math.sqrt(2)) : Math.abs(width * Math.sqrt(2)); // Maintain 1:√2 ratio const maxY = end[1] > minY ? minY + height : minY - height; diff --git a/client/src/constants/map.ts b/client/src/constants/map.ts new file mode 100644 index 0000000..ce06d59 --- /dev/null +++ b/client/src/constants/map.ts @@ -0,0 +1,48 @@ +export const schemas = [ + '2018', + '2019', + '2020', + '2021', + '2022', + '2023', + '2024', +] + +export const scaleOptions = [ + { + label: '1:500000', + value: '500' + }, + { + label: '1:100000', + value: '250' + }, + { + label: '1:50000', + value: '50' + }, + { + label: '1:25000', + value: '25' + }, + { + label: '1:10000', + value: '10' + }, +] + +export const satMapsProviders = [ + { label: 'Google', value: 'google' }, + { label: 'Яндекс', value: 'yandex' }, + { label: 'Подложка', value: 'custom' }, + { label: 'Static', value: 'static' } +] + +export const printDimensions = { + a0: [1189, 841], + a1: [841, 594], + a2: [594, 420], + a3: [420, 297], + a4: [297, 210], + a5: [210, 148], +} \ No newline at end of file diff --git a/client/src/store/map.ts b/client/src/store/map.ts index ba87716..648f033 100644 --- a/client/src/store/map.ts +++ b/client/src/store/map.ts @@ -25,9 +25,14 @@ import { transform } from 'ol/proj'; import { applyTransformations, calculateTransformations, fixedAspectRatioBox, zoomToFeature } from '../components/map/mapUtils'; import { setCurrentObjectId, setSelectedRegion } from './objects'; import View from 'ol/View'; +import { getPrintOrientation } from './print'; export type Mode = 'edit' | 'view' | 'print' +export type PrintScale = '500' | '250' | '50' | '25' | '10' + +export type PrintOrientation = 'horizontal' | 'vertical' + interface MapState { id: Record; } @@ -224,7 +235,22 @@ export const initializeMapState = ( const printAreaDraw = new Draw({ source: printSource, type: 'Circle', - geometryFunction: fixedAspectRatioBox + style: (feature) => { + return new Style({ + fill: new Fill({ + color: "rgba(0, 0, 255, 0.3)", // Semi-transparent blue fill + }), + stroke: new Stroke({ + color: "blue", + width: 2, + }), + image: undefined, // 🚀 This removes the default point! + }); + }, + geometryFunction: function (coords, geom) { + const printOrientation = getPrintOrientation() + return fixedAspectRatioBox(coords, geom, printOrientation) + }, }) printAreaDraw.on('drawend', (e) => { @@ -396,17 +422,59 @@ export const initializeMapState = ( overlayLayer: overlayLayer, regionSelect: regionSelect, lineSelect: lineSelect, + previousView: null, printArea: null, printLayer: printLayer, printSource: printSource, printAreaDraw: printAreaDraw, - printPreviewSize: [640, 320] + printPreviewSize: [640, 320], + printDim: 'a4', + printOrientation: 'horizontal', + printRes: 72, + printScale: '250', + printScaleLine: true } } } }) } +export const setPrintOrientation = (id: string, orientation: PrintOrientation) => useMapStore.setState((state) => { + return { + id: { + ...state.id, + [id]: { ...state.id[id], printOrientation: orientation } + } + } +}) + +export const setPrintScaleLine = (id: string, bool: boolean) => useMapStore.setState((state) => { + return { + id: { + ...state.id, + [id]: { ...state.id[id], printScaleLine: bool } + } + } +}) + +export const setPrintScale = (id: string, printScale: PrintScale) => useMapStore.setState((state) => { + return { + id: { + ...state.id, + [id]: { ...state.id[id], printScale: printScale } + } + } +}) + +export const setPreviousView = (id: string, view: View | undefined | null) => useMapStore.setState((state) => { + return { + id: { + ...state.id, + [id]: { ...state.id[id], previousView: view } + } + } +}) + export const clearPrintArea = (id: string) => useMapStore.setState((state) => { state.id[id].printSource.clear() diff --git a/client/src/store/print.ts b/client/src/store/print.ts new file mode 100644 index 0000000..6d1c860 --- /dev/null +++ b/client/src/store/print.ts @@ -0,0 +1,23 @@ +import { create } from 'zustand'; + +export type PrintOrientation = 'horizontal' | 'vertical' + +export type PrintFormat = 'a0' | 'a1' | 'a2' | 'a3' | 'a4' | 'a5' +export const printResolutions = ['72', '150', '200', '300'] + +export interface PrintState { + printFormat: PrintFormat; + printOrientation: PrintOrientation; + printResolution: number; +} + +export const usePrintStore = create(() => ({ + printFormat: 'a4', + printOrientation: 'horizontal', + printResolution: 72 +})) + +export const getPrintOrientation = () => usePrintStore.getState().printOrientation +export const setPrintFormat = (format: PrintFormat) => usePrintStore.setState(() => ({ printFormat: format })) +export const setPrintOrientation = (orientation: PrintOrientation) => usePrintStore.setState(() => ({ printOrientation: orientation })) +export const setPrintResolution = (resolution: number) => usePrintStore.setState(() => ({ printResolution: resolution })) \ No newline at end of file