import { useRef, useState, useEffect, RefObject } from 'react'; import { useTranslation } from 'react-i18next'; import initI18n from './i18n'; import MapView from '@arcgis/core/views/MapView'; import esriConfig from '@arcgis/core/config.js'; import * as print from '@arcgis/core/rest/print.js'; import PrintParameters from '@arcgis/core/rest/support/PrintParameters.js'; import PrintTemplate from '@arcgis/core/rest/support/PrintTemplate.js'; import * as reactiveUtils from '@arcgis/core/core/reactiveUtils.js'; import Point from '@arcgis/core/geometry/Point.js'; // set assets path for ArcGIS Maps SDK widgets esriConfig.assetsPath = './assets'; // set local assets for Calcite components import { setAssetPath } from '@esri/calcite-components/dist/components'; setAssetPath(window.location.href); // import Calcite components import '@esri/calcite-components/dist/components/calcite-button'; import '@esri/calcite-components/dist/components/calcite-panel'; import '@esri/calcite-components/dist/components/calcite-input-text'; import '@esri/calcite-components/dist/components/calcite-label'; import '@esri/calcite-components/dist/components/calcite-select'; import '@esri/calcite-components/dist/components/calcite-option'; import '@esri/calcite-components/dist/components/calcite-progress'; import { CalciteButton, CalcitePanel, CalciteInputText, CalciteLabel, CalciteSelect, CalciteOption, CalciteProgress, } from '@esri/calcite-components-react'; // helper function to clamp coordinates to visible area function clamp(value: number, from: number, to: number) { return value < from ? from : value > to ? to : value; } // print service URL const printURL = 'https://gis.geosphere.at/maps/rest/services/tools/printing/GPServer/Export%20Web%20Map'; // available scales const scales = [10000, 25000, 50000, 100000, 200000, 500000, 1000000, 3500000]; // available formats interface Format { [formatName: string]: number[]; } // dimensions width and height in mm const formats: Format = { 'A4 Hochformat': [190, 265], 'A4 Hochformat mit Legende': [190, 225], 'A4 Querformat': [277, 178], 'A4 Querformat mit Legende': [277, 140], 'A3 Hochformat': [277, 385], 'A3 Hochformat mit Legende': [277, 335], 'A3 Querformat': [400, 264], 'A3 Querformat mit Legende': [400, 215], }; // load localized strings const match = location.pathname.match(/\/(\w+)/); if (match && match.length > 1) { const locale = match[1]; initI18n(locale); } export default function Print({ view, maskRoot }: { view: MapView; maskRoot: RefObject }) { const [title, setTitle] = useState('GeoSphere Austria'); const [format, setFormat] = useState(Object.keys(formats)[0]); const [scale, setScale] = useState(scales[0]); const [printing, setPrinting] = useState(false); const rendered = useRef(false); const { t } = useTranslation(); const currentScale = useRef(); currentScale.current = scale; const currentFormat = useRef(); currentFormat.current = format; const controllerRef = useRef(null); const handlePrint = () => { setPrinting(true); const template = new PrintTemplate({ layout: format, format: 'pdf', layoutOptions: { titleText: title, scalebarUnit: 'Kilometers', customTextElements: [], }, exportOptions: { dpi: 96, }, scalePreserved: true, outScale: scale, }); const params = new PrintParameters({ view, template, }); controllerRef.current = new AbortController(); print.execute(printURL, params, { signal: controllerRef.current.signal }).then(printResult).catch(printError); }; function printResult(result: any) { const filename = 'GeoSphere_Maps_Print.pdf'; fetch(result.url) .then((res) => res.blob()) .then((blob) => { const element = document.createElement('a'); element.setAttribute('href', URL.createObjectURL(blob)); element.setAttribute('download', filename); document.body.appendChild(element); element.click(); document.body.removeChild(element); setPrinting(false); }); } function printError(err: any) { if (err.name === 'AbortError') { // console.log('Request aborted'); } else { console.error('Error encountered: ', err); } } const handleTitleChange = (event: any) => { setTitle(event.target.value); }; const handleFormatChange = (event: any) => { const newFormat = event.target.value; setFormat(newFormat); updatePreview(scale, newFormat); }; const handleScaleChange = (event: any) => { const newScale = parseInt(event.target.value); setScale(newScale); updatePreview(newScale, format); }; const updatePreview = (newScale: number, newFormat: string) => { const width = (formats[newFormat][0] * newScale) / 1000; const height = (formats[newFormat][1] * newScale) / 1000; setMask(width, height); }; // set the mask for print preview const setMask = (width: number, height: number) => { const center = view.center; if (center) { const xmin = center.x - width / 2; const xmax = center.x + width / 2; const ymin = center.y - height / 2; const ymax = center.y + height / 2; const upperLeft = view.toScreen( new Point({ x: xmin, y: ymax, spatialReference: view.spatialReference, }) ); const lowerRight = view.toScreen( new Point({ x: xmax, y: ymin, spatialReference: view.spatialReference, }) ); const left = clamp(Math.round(upperLeft.x), 0, view.width); const top = clamp(Math.round(upperLeft.y), 0, view.height); const maskWidth = clamp(Math.round(lowerRight.x - upperLeft.x), 0, view.width); const maskHeight = clamp(Math.round(lowerRight.y - upperLeft.y), 0, view.height); if (maskRoot && maskRoot.current) { maskRoot.current.style.left = left + 'px'; maskRoot.current.style.top = top + 'px'; maskRoot.current.style.width = maskWidth + 'px'; maskRoot.current.style.height = maskHeight + 'px'; } } }; useEffect(() => { if (currentScale.current && currentFormat.current) { updatePreview(currentScale.current, currentFormat.current); } let handle: any; if (!rendered.current) { handle = reactiveUtils.watch( () => view?.extent, () => { if (currentScale.current && currentFormat.current) { updatePreview(currentScale.current, currentFormat.current); } } ); } rendered.current = true; return () => { handle?.remove(); }; }); const handleAbort = () => { if (controllerRef.current) { controllerRef.current.abort(); setPrinting(false); } }; return (
{printing && }
{t('print.titleLabel')} {t('print.formatLabel')} {Object.keys(formats).map((formatName) => { return ( {t(`print.formats.${formatName}`)} ); })} {t('print.scaleLabel')} {scales.map((num) => ( {`1:${num .toString() .match(/(\d+?)(?=(\d{3})+(?!\d)|$)/g) ?.join('.')}`} ))} {!printing ? ( {t('print.button.print')} ) : ( <> {t('print.button.cancel')} )}
); }