Files
tests/client/src/components/map/MapComponent.tsx
2024-08-20 17:34:21 +09:00

176 lines
6.1 KiB
TypeScript

import { useEffect, useRef, useState } from 'react'
import GeoJSON from 'ol/format/GeoJSON'
import 'ol/ol.css'
import Map from 'ol/Map'
import View from 'ol/View'
import { Draw, Modify, Snap } from 'ol/interaction'
import { OSM, Vector as VectorSource } from 'ol/source'
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
import { transform, transformExtent } from 'ol/proj'
import { Divider, IconButton, Stack } from '@mui/material'
import { Adjust, Api, CircleOutlined, RectangleOutlined, Timeline, Undo, Warning } from '@mui/icons-material'
import { Type } from 'ol/geom/Geometry'
const MapComponent = () => {
const mapElement = useRef<HTMLDivElement | null>(null)
const [currentTool, setCurrentTool] = useState<Type>('Point')
const map = useRef<Map | null>(null)
const source = useRef<VectorSource>(new VectorSource())
const draw = useRef<Draw | null>(null)
const snap = useRef<Snap | null>(null)
const drawingLayer = useRef<VectorLayer | null>(null)
const addInteractions = () => {
draw.current = new Draw({
source: source.current,
type: currentTool,
})
map?.current?.addInteraction(draw.current)
snap.current = new Snap({ source: source.current })
map?.current?.addInteraction(snap.current)
}
// Function to save features to localStorage
const saveFeatures = () => {
const features = drawingLayer.current?.getSource()?.getFeatures()
if (features && features.length > 0) {
const geoJSON = new GeoJSON()
const featuresJSON = geoJSON.writeFeatures(features)
localStorage.setItem('savedFeatures', featuresJSON)
}
}
// Function to load features from localStorage
const loadFeatures = () => {
const savedFeatures = localStorage.getItem('savedFeatures')
if (savedFeatures) {
const geoJSON = new GeoJSON()
const features = geoJSON.readFeatures(savedFeatures, {
featureProjection: 'EPSG:4326', // Ensure the projection is correct
})
source.current?.addFeatures(features) // Add features to the vector source
//drawingLayer.current?.getSource()?.changed()
}
}
useEffect(() => {
const geoLayer = new VectorLayer({
background: '#1a2b39',
source: new VectorSource({
url: 'https://openlayers.org/data/vector/ecoregions.json',
format: new GeoJSON(),
}),
style: {
'fill-color': ['string', ['get', 'COLOR'], '#eee'],
},
})
const raster = new TileLayer({
source: new OSM(),
})
drawingLayer.current = new VectorLayer({
source: source.current,
style: {
'fill-color': 'rgba(255, 255, 255, 0.2)',
'stroke-color': '#ffcc33',
'stroke-width': 2,
'circle-radius': 7,
'circle-fill-color': '#ffcc33',
},
})
// Center coordinates of Yakutia in EPSG:3857
const center = transform([129.7694, 66.9419], 'EPSG:4326', 'EPSG:3857')
// Extent for Yakutia in EPSG:4326
const extent4326 = [105.0, 55.0, 170.0, 75.0] // Approximate bounding box
// Transform extent to EPSG:3857
const extent = transformExtent(extent4326, 'EPSG:4326', 'EPSG:3857')
map.current = new Map({
layers: [geoLayer, raster, drawingLayer.current],
target: mapElement.current as HTMLDivElement,
view: new View({
center,
zoom: 4,
extent,
}),
})
const modify = new Modify({ source: source.current })
map.current.addInteraction(modify)
addInteractions()
loadFeatures()
return () => {
map?.current?.setTarget(undefined)
}
}, [])
useEffect(() => {
if (currentTool) {
if (draw.current) map?.current?.removeInteraction(draw.current)
if (snap.current) map?.current?.removeInteraction(snap.current)
addInteractions()
}
}, [currentTool])
return (
<div>
<Stack my={1} spacing={1} direction='row' divider={<Divider orientation='vertical' flexItem />}>
<IconButton onClick={() => {
fetch(`${import.meta.env.VITE_API_EMS_URL}/hello`, { method: 'GET' }).then(res => console.log(res))
}}>
<Api />
</IconButton>
<IconButton onClick={() => {
saveFeatures()
}}>
<Warning />
</IconButton>
<IconButton
onClick={() => {
draw.current?.removeLastPoint()
}}>
<Undo />
</IconButton>
<IconButton
sx={{ backgroundColor: currentTool === 'Point' ? 'Highlight' : 'transparent' }}
onClick={() => setCurrentTool('Point')}>
<Adjust />
</IconButton>
<IconButton
sx={{ backgroundColor: currentTool === 'LineString' ? 'Highlight' : 'transparent' }}
onClick={() => setCurrentTool('LineString')}>
<Timeline />
</IconButton>
<IconButton
sx={{ backgroundColor: currentTool === 'Polygon' ? 'Highlight' : 'transparent' }}
onClick={() => setCurrentTool('Polygon')}>
<RectangleOutlined />
</IconButton>
<IconButton
sx={{ backgroundColor: currentTool === 'Circle' ? 'Highlight' : 'transparent' }}
onClick={() => setCurrentTool('Circle')}>
<CircleOutlined />
</IconButton>
</Stack>
<div ref={mapElement} style={{ width: '100%', height: '400px' }}></div>
</div>
);
};
export default MapComponent