Move initi18n. Update search component

This commit is contained in:
Fuhrmann 2023-09-29 13:18:55 +02:00
parent c8046c39f1
commit a810f87461
7 changed files with 124 additions and 114 deletions

View File

@ -1,7 +1,6 @@
import { useRef, useEffect, RefObject } from 'react'; import { useRef, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import initI18n from './i18n';
import MapView from '@arcgis/core/views/MapView'; import MapView from '@arcgis/core/views/MapView';
import Basemap from '@arcgis/core/Basemap'; import Basemap from '@arcgis/core/Basemap';
@ -13,13 +12,6 @@ import TileLayer from '@arcgis/core/layers/TileLayer';
const BEVURL = 'https://maps.bev.gv.at/tiles/{z}/{x}/{y}.png'; const BEVURL = 'https://maps.bev.gv.at/tiles/{z}/{x}/{y}.png';
// load localized strings
const match = location.pathname.match(/\/(\w+)/);
if (match && match.length > 1) {
const locale = match[1];
initI18n(locale);
}
export default function Basemaps({ view }: { view: MapView }) { export default function Basemaps({ view }: { view: MapView }) {
const basemapGalleryContainer = useRef<HTMLDivElement | null>(null); const basemapGalleryContainer = useRef<HTMLDivElement | null>(null);
const rendered = useRef<boolean>(false); const rendered = useRef<boolean>(false);

View File

@ -4,7 +4,7 @@ import { initReactI18next } from 'react-i18next';
import deJSON from './dictionaries/de.json'; import deJSON from './dictionaries/de.json';
import enJSON from './dictionaries/en.json'; import enJSON from './dictionaries/en.json';
// the translations // define translations
const resources = { const resources = {
en: { en: {
translation: enJSON, translation: enJSON,
@ -14,15 +14,14 @@ const resources = {
}, },
}; };
// initialize i18n with given locale
const initI18n = (locale) => const initI18n = (locale) =>
i18n i18n.use(initReactI18next).init({
.use(initReactI18next) // passes i18n down to react-i18next resources,
.init({ lng: locale,
resources, interpolation: {
lng: locale, escapeValue: false,
interpolation: { },
escapeValue: false, // react already safes from xss });
},
});
export default initI18n; export default initI18n;

View File

@ -1,7 +1,6 @@
import { useRef, useEffect, RefObject } from 'react'; import { useRef, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import initI18n from './i18n';
import MapView from '@arcgis/core/views/MapView'; import MapView from '@arcgis/core/views/MapView';
import LayerList from '@arcgis/core/widgets/LayerList'; import LayerList from '@arcgis/core/widgets/LayerList';
@ -11,14 +10,7 @@ import MapImageLayer from '@arcgis/core/layers/MapImageLayer';
import Sublayer from '@arcgis/core/layers/support/Sublayer'; import Sublayer from '@arcgis/core/layers/support/Sublayer';
import ButtonMenuItem from '@arcgis/core/widgets/FeatureTable/Grid/support/ButtonMenuItem'; import ButtonMenuItem from '@arcgis/core/widgets/FeatureTable/Grid/support/ButtonMenuItem';
// load localized strings export default function Layers({ view, tableRoot }: { view: MapView; tableRoot: HTMLDivElement }) {
const match = location.pathname.match(/\/(\w+)/);
if (match && match.length > 1) {
const locale = match[1];
initI18n(locale);
}
export default function Layers({ view, tableRoot }: { view: MapView; tableRoot: RefObject<HTMLDivElement> }) {
const htmlDiv = useRef<HTMLDivElement>(null); const htmlDiv = useRef<HTMLDivElement>(null);
const featureTable = useRef<FeatureTable | null>(null); const featureTable = useRef<FeatureTable | null>(null);
const rendered = useRef<boolean>(false); const rendered = useRef<boolean>(false);
@ -33,9 +25,9 @@ export default function Layers({ view, tableRoot }: { view: MapView; tableRoot:
const tableContainer = document.createElement('div'); const tableContainer = document.createElement('div');
tableContainer.className = 'h-full w-full'; tableContainer.className = 'h-full w-full';
if (tableRoot && tableRoot.current) { if (tableRoot) {
tableRoot.current.classList.remove('hidden'); tableRoot.classList.remove('hidden');
tableRoot.current.append(tableContainer); tableRoot.append(tableContainer);
} }
const featureLayer = await layer.createFeatureLayer(); const featureLayer = await layer.createFeatureLayer();
@ -51,8 +43,8 @@ export default function Layers({ view, tableRoot }: { view: MapView; tableRoot:
iconClass: 'esri-icon-close', iconClass: 'esri-icon-close',
clickFunction: function () { clickFunction: function () {
featureTable.current?.destroy(); featureTable.current?.destroy();
if (tableRoot && tableRoot.current) { if (tableRoot) {
tableRoot.current.classList.add('hidden'); tableRoot.classList.add('hidden');
} }
}, },
} as unknown as ButtonMenuItem, } as unknown as ButtonMenuItem,

View File

@ -1,15 +1,15 @@
import { useRef, useState, useEffect, lazy } from 'react'; import { useRef, useState, useEffect } from 'react';
import { Root, createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import dynamic from 'next/dynamic';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import initI18n from './i18n';
import MapView from '@arcgis/core/views/MapView'; import MapView from '@arcgis/core/views/MapView';
import WebMap from '@arcgis/core/WebMap'; import WebMap from '@arcgis/core/WebMap';
import esriConfig from '@arcgis/core/config'; import esriConfig from '@arcgis/core/config';
import ScaleBar from '@arcgis/core/widgets/ScaleBar'; import ScaleBar from '@arcgis/core/widgets/ScaleBar';
import Legend from '@arcgis/core/widgets/Legend'; import Legend from '@arcgis/core/widgets/Legend';
import WMTSLayer from '@arcgis/core/layers/WMTSLayer';
import VectorTileLayer from '@arcgis/core/layers/VectorTileLayer'; import VectorTileLayer from '@arcgis/core/layers/VectorTileLayer';
import TileLayer from '@arcgis/core/layers/TileLayer'; import TileLayer from '@arcgis/core/layers/TileLayer';
import Map from '@arcgis/core/Map.js'; import Map from '@arcgis/core/Map.js';
@ -23,11 +23,11 @@ esriConfig.assetsPath = './assets';
const webMapDEID = '7d0768f73d3e4be2b32c22274c600cb3'; const webMapDEID = '7d0768f73d3e4be2b32c22274c600cb3';
const webMapENID = 'dbf5532d06954c6a989d4f022de83f70'; const webMapENID = 'dbf5532d06954c6a989d4f022de83f70';
// lazy load print component // lazy load components
const Print = lazy(() => import('./print')); const Print = dynamic(() => import('./print'));
const Layers = lazy(() => import('./layer-list')); const Layers = dynamic(() => import('./layer-list'));
const Basemaps = lazy(() => import('./basemap-list')); const Basemaps = dynamic(() => import('./basemap-list'));
const Search = lazy(() => import('./search')); const Search = dynamic(() => import('./search'));
// import Calcite components // import Calcite components
import '@esri/calcite-components/dist/calcite/calcite.css'; import '@esri/calcite-components/dist/calcite/calcite.css';
@ -49,15 +49,8 @@ import {
CalcitePanel, CalcitePanel,
} from '@esri/calcite-components-react'; } from '@esri/calcite-components-react';
// load localized strings
const match = location.pathname.match(/\/(\w+)/);
if (match && match.length > 1) {
const locale = match[1];
initI18n(locale);
}
export default function MapComponent({ locale }: { locale: string }) { export default function MapComponent({ locale }: { locale: string }) {
const printRoot = useRef<Root | null>(null); const legendRoot = useRef<HTMLDivElement>(null);
const maskRoot = useRef<HTMLDivElement>(null); const maskRoot = useRef<HTMLDivElement>(null);
const tableRoot = useRef<HTMLDivElement>(null); const tableRoot = useRef<HTMLDivElement>(null);
const mapView = useRef<MapView | null>(null); const mapView = useRef<MapView | null>(null);
@ -81,14 +74,6 @@ export default function MapComponent({ locale }: { locale: string }) {
}, },
}); });
// const wmtsLayer = new WMTSLayer({
// url: 'https://mapsneu.wien.gv.at/basemapneu',
// });
// const basemap = new Basemap({
// baseLayers: [wmtsLayer],
// });
const lightgrayBase = new VectorTileLayer({ const lightgrayBase = new VectorTileLayer({
url: 'https://gis.geosphere.at/portal/sharing/rest/content/items/291da5eab3a0412593b66d384379f89f/resources/styles/root.json', url: 'https://gis.geosphere.at/portal/sharing/rest/content/items/291da5eab3a0412593b66d384379f89f/resources/styles/root.json',
opacity: 0.5, opacity: 0.5,
@ -124,6 +109,7 @@ export default function MapComponent({ locale }: { locale: string }) {
width: 5000, width: 5000,
}, },
}, },
dockEnabled: true,
}, },
extent: { extent: {
ymax: 6424330, ymax: 6424330,
@ -144,6 +130,7 @@ export default function MapComponent({ locale }: { locale: string }) {
createRoot(document.createElement('div')).render(<Search view={view}></Search>); createRoot(document.createElement('div')).render(<Search view={view}></Search>);
}); });
// add further map related UI components
const scaleBar = new ScaleBar({ const scaleBar = new ScaleBar({
view: view, view: view,
unit: 'metric', unit: 'metric',
@ -151,10 +138,12 @@ export default function MapComponent({ locale }: { locale: string }) {
view.ui.add([scaleBar], 'bottom-left'); view.ui.add([scaleBar], 'bottom-left');
new Legend({ if (legendRoot.current) {
view: view, new Legend({
container: 'legend-container', view: view,
}); container: legendRoot.current,
});
}
} }
return () => {}; return () => {};
@ -211,7 +200,6 @@ export default function MapComponent({ locale }: { locale: string }) {
return ( return (
<div> <div>
<CalciteShell contentBehind> <CalciteShell contentBehind>
<h2 id="header-title" slot="header"></h2>
<CalciteShellPanel slot="panel-start" displayMode="float"> <CalciteShellPanel slot="panel-start" displayMode="float">
<CalciteActionBar <CalciteActionBar
slot="action-bar" slot="action-bar"
@ -246,7 +234,9 @@ export default function MapComponent({ locale }: { locale: string }) {
</CalciteActionBar> </CalciteActionBar>
<CalcitePanel data-panel-id="layers" heading={t('layers.title')} hidden> <CalcitePanel data-panel-id="layers" heading={t('layers.title')} hidden>
{mapView.current && <Layers view={mapView.current} tableRoot={tableRoot}></Layers>} {mapView.current && tableRoot.current && (
<Layers view={mapView.current} tableRoot={tableRoot.current}></Layers>
)}
</CalcitePanel> </CalcitePanel>
<CalcitePanel data-panel-id="basemaps" heading={t('basemaps.title')} hidden> <CalcitePanel data-panel-id="basemaps" heading={t('basemaps.title')} hidden>
@ -254,11 +244,11 @@ export default function MapComponent({ locale }: { locale: string }) {
</CalcitePanel> </CalcitePanel>
<CalcitePanel data-panel-id="legend" heading={t('legend.title')} hidden> <CalcitePanel data-panel-id="legend" heading={t('legend.title')} hidden>
<div id="legend-container"></div> <div ref={legendRoot}></div>
</CalcitePanel> </CalcitePanel>
<CalcitePanel data-panel-id="print" heading={t('print.heading')} hidden> <CalcitePanel data-panel-id="print" heading={t('print.heading')} hidden>
{mapView.current && <Print view={mapView.current} maskRoot={maskRoot}></Print>} {mapView.current && maskRoot.current && <Print view={mapView.current} maskRoot={maskRoot.current}></Print>}
</CalcitePanel> </CalcitePanel>
<CalcitePanel data-panel-id="info" heading="Info" hidden> <CalcitePanel data-panel-id="info" heading="Info" hidden>

View File

@ -1,9 +1,11 @@
'use client'; 'use client';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import initI18n from './i18n';
const Map = dynamic(() => import('./map'), { ssr: false }); const Map = dynamic(() => import('./map'), { ssr: false });
export default function Home({ params: { locale } }: { params: { locale: string } }) { export default function Home({ params: { locale } }: { params: { locale: string } }) {
initI18n(locale);
return <Map locale={locale}></Map>; return <Map locale={locale}></Map>;
} }

View File

@ -1,7 +1,6 @@
import { useRef, useState, useEffect, RefObject } from 'react'; import { useRef, useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import initI18n from './i18n';
import MapView from '@arcgis/core/views/MapView'; import MapView from '@arcgis/core/views/MapView';
import esriConfig from '@arcgis/core/config.js'; import esriConfig from '@arcgis/core/config.js';
@ -64,14 +63,9 @@ const formats: Format = {
'A3 Querformat mit Legende': [400, 215], 'A3 Querformat mit Legende': [400, 215],
}; };
// load localized strings export default function Print({ view, maskRoot }: { view: MapView; maskRoot: HTMLDivElement }) {
const match = location.pathname.match(/\/(\w+)/); const { t } = useTranslation();
if (match && match.length > 1) {
const locale = match[1];
initI18n(locale);
}
export default function Print({ view, maskRoot }: { view: MapView; maskRoot: RefObject<HTMLDivElement> }) {
const [title, setTitle] = useState<string>('GeoSphere Austria'); const [title, setTitle] = useState<string>('GeoSphere Austria');
const [format, setFormat] = useState<string>(Object.keys(formats)[0]); const [format, setFormat] = useState<string>(Object.keys(formats)[0]);
const [scale, setScale] = useState<number>(scales[0]); const [scale, setScale] = useState<number>(scales[0]);
@ -79,8 +73,6 @@ export default function Print({ view, maskRoot }: { view: MapView; maskRoot: Ref
const rendered = useRef<boolean>(false); const rendered = useRef<boolean>(false);
const { t } = useTranslation();
const currentScale = useRef<number>(); const currentScale = useRef<number>();
currentScale.current = scale; currentScale.current = scale;
@ -191,11 +183,11 @@ export default function Print({ view, maskRoot }: { view: MapView; maskRoot: Ref
const maskWidth = clamp(Math.round(lowerRight.x - upperLeft.x), 0, view.width); const maskWidth = clamp(Math.round(lowerRight.x - upperLeft.x), 0, view.width);
const maskHeight = clamp(Math.round(lowerRight.y - upperLeft.y), 0, view.height); const maskHeight = clamp(Math.round(lowerRight.y - upperLeft.y), 0, view.height);
if (maskRoot && maskRoot.current) { if (maskRoot) {
maskRoot.current.style.left = left + 'px'; maskRoot.style.left = left + 'px';
maskRoot.current.style.top = top + 'px'; maskRoot.style.top = top + 'px';
maskRoot.current.style.width = maskWidth + 'px'; maskRoot.style.width = maskWidth + 'px';
maskRoot.current.style.height = maskHeight + 'px'; maskRoot.style.height = maskHeight + 'px';
} }
} }
}; };

View File

@ -2,7 +2,6 @@ import { useRef, useState, useEffect } from 'react';
import { Root, createRoot } from 'react-dom/client'; import { Root, createRoot } from 'react-dom/client';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import initI18n from './i18n';
// import Calcite components // import Calcite components
import '@esri/calcite-components/dist/calcite/calcite.css'; import '@esri/calcite-components/dist/calcite/calcite.css';
@ -37,13 +36,19 @@ import Polygon from '@arcgis/core/geometry/Polygon';
import { geodesicBuffer } from '@arcgis/core/geometry/geometryEngine'; import { geodesicBuffer } from '@arcgis/core/geometry/geometryEngine';
import MapImageLayer from '@arcgis/core/layers/MapImageLayer'; import MapImageLayer from '@arcgis/core/layers/MapImageLayer';
import SimpleFillSymbol from '@arcgis/core/symbols/SimpleFillSymbol'; import SimpleFillSymbol from '@arcgis/core/symbols/SimpleFillSymbol';
import SimpleLineSymbol from '@arcgis/core/symbols/SimpleLineSymbol';
import SimpleMarkerSymbol from '@arcgis/core/symbols/SimpleMarkerSymbol'; import SimpleMarkerSymbol from '@arcgis/core/symbols/SimpleMarkerSymbol';
import Sublayer from '@arcgis/core/layers/support/Sublayer'; import Sublayer from '@arcgis/core/layers/support/Sublayer';
import PopupTemplate from '@arcgis/core/PopupTemplate'; import PopupTemplate from '@arcgis/core/PopupTemplate';
// create feaure layer from URL of data index layer // create feaure layer from URL of data index layer
const datenIndexURL = 'https://gis.geosphere.at/maps/rest/services/datenindex/raster_5000/MapServer/0'; const datenIndexURL = 'https://gis.geosphere.at/maps/rest/services/datenindex/raster_5000/MapServer/0';
const indexLayer = new FeatureLayer({ url: datenIndexURL, title: 'Datenindex 1:5.000', opacity: 0 }); const indexLayer = new FeatureLayer({
url: datenIndexURL,
title: 'Datenindex 1:5.000',
opacity: 0,
legendEnabled: false,
});
// custom type definitions // custom type definitions
interface CustomLayer { interface CustomLayer {
@ -65,13 +70,6 @@ interface LayerToFeaturesMap {
[key: string]: string[]; [key: string]: string[];
} }
// load localized strings
const match = location.pathname.match(/\/(\w+)/);
if (match && match.length > 1) {
const locale = match[1];
initI18n(locale);
}
// custom React component // custom React component
export default function SearchComponent({ view }: { view: MapView }) { export default function SearchComponent({ view }: { view: MapView }) {
const [currentTarget, setCurrentTarget] = useState<Graphic | null>(null); const [currentTarget, setCurrentTarget] = useState<Graphic | null>(null);
@ -140,34 +138,78 @@ export default function SearchComponent({ view }: { view: MapView }) {
const sr = mapImageLayer?.spatialReference; const sr = mapImageLayer?.spatialReference;
const geometry = json.feature?.geometry; const geometry = json.feature?.geometry;
if (geometry.x && geometry.y) { view.graphics.removeAll();
view.goTo( const higlightOptions = view.highlightOptions;
new Point({ if (geometry.x && geometry.y && higlightOptions && higlightOptions.color && higlightOptions.fillOpacity) {
x: geometry.x, const point = new Point({
y: geometry.y, x: geometry.x,
spatialReference: sr, y: geometry.y,
}) spatialReference: sr,
); });
const graphic = new Graphic({
geometry: point,
symbol: {
type: 'simple-marker',
style: 'circle',
size: '8px',
color: [
higlightOptions.color.r,
higlightOptions.color.g,
higlightOptions.color.b,
higlightOptions.fillOpacity,
],
outline: {
color: [higlightOptions.color.r, higlightOptions.color.g, higlightOptions.color.b, higlightOptions.color.a],
width: 1,
},
} as unknown as SimpleMarkerSymbol,
});
view.graphics.add(graphic);
view.goTo(geodesicBuffer(point, 1000, 'meters'));
return; return;
} }
if (geometry.paths) { if (geometry.paths && higlightOptions && higlightOptions.color && higlightOptions.fillOpacity) {
view.goTo( const polyline = new Polyline({
new Polyline({ paths: geometry.paths,
paths: geometry.paths, spatialReference: sr,
spatialReference: sr, });
}) const graphic = new Graphic({
); geometry: polyline,
symbol: {
type: 'simple-line',
color: [higlightOptions.color.r, higlightOptions.color.g, higlightOptions.color.b],
width: '2px',
} as unknown as SimpleLineSymbol,
});
view.graphics.add(graphic);
view.goTo(geodesicBuffer(polyline.extent.center, 5000, 'meters'));
return; return;
} }
if (geometry.rings) { if (geometry.rings && higlightOptions && higlightOptions.color && higlightOptions.fillOpacity) {
view.goTo( const polygon = new Polygon({
new Polygon({ rings: geometry.rings,
rings: geometry.rings, spatialReference: sr,
spatialReference: sr, });
}) const graphic = new Graphic({
); geometry: polygon,
symbol: {
type: 'simple-fill',
color: [
higlightOptions.color.r,
higlightOptions.color.g,
higlightOptions.color.b,
higlightOptions.fillOpacity,
],
outline: {
color: [higlightOptions.color.r, higlightOptions.color.g, higlightOptions.color.b, higlightOptions.color.a],
width: 1,
},
} as unknown as SimpleFillSymbol,
});
view.graphics.add(graphic);
view.goTo(polygon);
return; return;
} }
}; };
@ -587,6 +629,7 @@ export default function SearchComponent({ view }: { view: MapView }) {
} else { } else {
location = currentGraphicRef.current?.geometry; location = currentGraphicRef.current?.geometry;
} }
view.openPopup({ view.openPopup({
title: t('search.popup-title'), title: t('search.popup-title'),
content: contentDiv, content: contentDiv,