Remove @mui, move states into zustand store
This commit is contained in:
@ -2,13 +2,13 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import 'ol/ol.css'
|
||||
import Map from 'ol/Map'
|
||||
import View from 'ol/View'
|
||||
import { Draw, Modify, Select, Snap, Translate } from 'ol/interaction'
|
||||
import { DragBox, Draw, Modify, Select, Snap, Translate } from 'ol/interaction'
|
||||
import { ImageStatic, OSM, Vector as VectorSource, XYZ } from 'ol/source'
|
||||
import { Tile as TileLayer, VectorImage, Vector as VectorLayer } from 'ol/layer'
|
||||
import { click, never, platformModifierKeyOnly, primaryAction, shiftKeyOnly } from 'ol/events/condition'
|
||||
import Feature from 'ol/Feature'
|
||||
import { IRectCoords, SatelliteMapsProvider } from '../../interfaces/map'
|
||||
import { Extent } from 'ol/extent'
|
||||
import { Extent, getWidth } from 'ol/extent'
|
||||
import { drawingLayerStyle, highlightStyleRed, highlightStyleYellow, overlayStyle, regionsLayerStyle } from './MapStyles'
|
||||
import { googleMapsSatelliteSource, regionsLayerSource, yandexMapsSatelliteSource } from './MapSources'
|
||||
import ImageLayer from 'ol/layer/Image'
|
||||
@ -22,7 +22,7 @@ import { get, transform } from 'ol/proj'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from '../../http/axiosInstance'
|
||||
import { BASE_URL } from '../../constants'
|
||||
import { Accordion, ActionIcon, Autocomplete, Box, CloseButton, Flex, Select as MantineSelect, MantineStyleProp, rem, ScrollAreaAutosize, Slider, useMantineColorScheme, Portal, Timeline, Text, Stack, NavLink, Grid, Checkbox } from '@mantine/core'
|
||||
import { Accordion, ActionIcon, Autocomplete, Box, CloseButton, Flex, Select as MantineSelect, MantineStyleProp, rem, ScrollAreaAutosize, Slider, useMantineColorScheme, Portal, Timeline, Text, Stack, NavLink, Checkbox } from '@mantine/core'
|
||||
import { IconPlus, IconSearch, IconSettings, IconTable, IconUpload } from '@tabler/icons-react'
|
||||
import { getGridCellPosition } from './mapUtils'
|
||||
import { IFigure, ILine } from '../../interfaces/gis'
|
||||
@ -32,7 +32,7 @@ import { IObjectParam } from '../../interfaces/objects'
|
||||
import MapToolbar from './MapToolbar/MapToolbar'
|
||||
import MapStatusbar from './MapStatusbar/MapStatusbar'
|
||||
import { measureStyleFunction, modifyStyle } from './Measure/MeasureStyles'
|
||||
import { useMapStore } from '../../store/map'
|
||||
import { setCurrentCoordinate, setCurrentX, setCurrentY, setCurrentZ, setSatMapsProvider, useMapStore } from '../../store/map'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useThrottle } from '@uidotdev/usehooks'
|
||||
import ObjectTree from '../Tree/ObjectTree'
|
||||
@ -42,6 +42,8 @@ import { setCurrentObjectId, setSelectedCity, setSelectedYear, useObjectsStore }
|
||||
const citySettings = [
|
||||
{
|
||||
city_id: 145,
|
||||
image_width: 8500,
|
||||
image_height: 12544,
|
||||
// scale: 10000,
|
||||
scale: 9000,
|
||||
offset_x: 14442665.697619518,
|
||||
@ -51,14 +53,11 @@ const citySettings = [
|
||||
]
|
||||
|
||||
const MapComponent = () => {
|
||||
|
||||
// States
|
||||
const { selectedCity, selectedYear, currentObjectId } = useObjectsStore()
|
||||
|
||||
const mapState = useMapStore()
|
||||
|
||||
const [currentCoordinate, setCurrentCoordinate] = useState<Coordinate | null>(null)
|
||||
const [currentZ, setCurrentZ] = useState<number | undefined>(undefined)
|
||||
const [currentX, setCurrentX] = useState<number | undefined>(undefined)
|
||||
const [currentY, setCurrentY] = useState<number | undefined>(undefined)
|
||||
const { currentTool, satMapsProvider } = useMapStore()
|
||||
///
|
||||
|
||||
const [file, setFile] = useState<File | null>(null)
|
||||
const [polygonExtent, setPolygonExtent] = useState<Extent | undefined>(undefined)
|
||||
@ -85,12 +84,85 @@ const MapComponent = () => {
|
||||
|
||||
/////
|
||||
|
||||
// Box selection
|
||||
|
||||
const dragBox = useRef(new DragBox({
|
||||
condition: platformModifierKeyOnly
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
if (dragBox.current) {
|
||||
dragBox.current.on('boxend', function () {
|
||||
const boxExtent = dragBox.current.getGeometry().getExtent();
|
||||
|
||||
// if the extent crosses the antimeridian process each world separately
|
||||
const worldExtent = map.current?.getView().getProjection().getExtent();
|
||||
|
||||
if (worldExtent) {
|
||||
const worldWidth = getWidth(worldExtent);
|
||||
const startWorld = Math.floor((boxExtent[0] - worldExtent[0]) / worldWidth);
|
||||
const endWorld = Math.floor((boxExtent[2] - worldExtent[0]) / worldWidth);
|
||||
|
||||
for (let world = startWorld; world <= endWorld; ++world) {
|
||||
const left = Math.max(boxExtent[0] - world * worldWidth, worldExtent[0]);
|
||||
const right = Math.min(boxExtent[2] - world * worldWidth, worldExtent[2]);
|
||||
const extent = [left, boxExtent[1], right, boxExtent[3]];
|
||||
|
||||
const boxFeatures = vectorSource
|
||||
.getFeaturesInExtent(extent)
|
||||
.filter(
|
||||
(feature) =>
|
||||
!selectedFeatures.getArray().includes(feature) &&
|
||||
feature.getGeometry().intersectsExtent(extent),
|
||||
);
|
||||
|
||||
// features that intersect the box geometry are added to the
|
||||
// collection of selected features
|
||||
|
||||
// if the view is not obliquely rotated the box geometry and
|
||||
// its extent are equalivalent so intersecting features can
|
||||
// be added directly to the collection
|
||||
const rotation = map.getView().getRotation();
|
||||
const oblique = rotation % (Math.PI / 2) !== 0;
|
||||
|
||||
// when the view is obliquely rotated the box extent will
|
||||
// exceed its geometry so both the box and the candidate
|
||||
// feature geometries are rotated around a common anchor
|
||||
// to confirm that, with the box geometry aligned with its
|
||||
// extent, the geometries intersect
|
||||
if (oblique) {
|
||||
const anchor = [0, 0];
|
||||
const geometry = dragBox.getGeometry().clone();
|
||||
geometry.translate(-world * worldWidth, 0);
|
||||
geometry.rotate(-rotation, anchor);
|
||||
const extent = geometry.getExtent();
|
||||
boxFeatures.forEach(function (feature) {
|
||||
const geometry = feature.getGeometry().clone();
|
||||
geometry.rotate(-rotation, anchor);
|
||||
if (geometry.intersectsExtent(extent)) {
|
||||
selectedFeatures.push(feature);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectedFeatures.extend(boxFeatures);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// clear selection when drawing a new box and when clicking on the map
|
||||
dragBox.current.on('boxstart', function () {
|
||||
selectedFeatures.clear();
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
/////
|
||||
|
||||
const mapElement = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const map = useRef<Map | null>(null)
|
||||
|
||||
const [satMapsProvider, setSatMapsProvider] = useState<SatelliteMapsProvider>('custom')
|
||||
|
||||
const gMapsSatSource = useRef<XYZ>(googleMapsSatelliteSource)
|
||||
|
||||
const customMapSource = useRef<XYZ>(new XYZ({
|
||||
@ -134,7 +206,7 @@ const MapComponent = () => {
|
||||
}
|
||||
}))
|
||||
|
||||
const figuresLayer = useRef<VectorImage>(new VectorImage({
|
||||
const figuresLayer = useRef<VectorLayer>(new VectorLayer({
|
||||
source: new VectorSource(),
|
||||
declutter: true,
|
||||
properties: {
|
||||
@ -143,7 +215,7 @@ const MapComponent = () => {
|
||||
}
|
||||
}))
|
||||
|
||||
const linesLayer = useRef<VectorImage>(new VectorImage({
|
||||
const linesLayer = useRef<VectorLayer>(new VectorLayer({
|
||||
source: new VectorSource(),
|
||||
declutter: true,
|
||||
properties: {
|
||||
@ -179,6 +251,14 @@ const MapComponent = () => {
|
||||
}
|
||||
}))
|
||||
|
||||
const staticMapLayer = useRef<ImageLayer<ImageStatic>>(new ImageLayer({
|
||||
properties: {
|
||||
id: uuidv4(),
|
||||
name: 'Static image'
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
// tile processing
|
||||
const handleImageDrop = useCallback((event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
@ -360,6 +440,7 @@ const MapComponent = () => {
|
||||
layers: [
|
||||
baseLayer.current,
|
||||
satLayer.current,
|
||||
staticMapLayer.current,
|
||||
regionsLayer.current,
|
||||
citiesLayer.current,
|
||||
linesLayer.current,
|
||||
@ -373,7 +454,8 @@ const MapComponent = () => {
|
||||
target: mapElement.current as HTMLDivElement,
|
||||
view: new View({
|
||||
center: transform([129.7466541, 62.083504], 'EPSG:4326', 'EPSG:3857'),//center: fromLonLat([130.401113, 67.797368]),
|
||||
zoom: 16,
|
||||
//zoom: 16,
|
||||
zoom: 15,
|
||||
maxZoom: 21,
|
||||
//extent: mapExtent,
|
||||
}),
|
||||
@ -415,7 +497,7 @@ const MapComponent = () => {
|
||||
|
||||
loadFeatures(drawingLayerSource)
|
||||
|
||||
regionsInit(map, selectedRegion, regionsLayer, setStatusText)
|
||||
regionsInit(map, selectedRegion, regionsLayer)
|
||||
|
||||
if (mapElement.current) {
|
||||
mapElement.current.addEventListener('dragover', (e) => {
|
||||
@ -435,7 +517,7 @@ const MapComponent = () => {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (mapState.currentTool) {
|
||||
if (currentTool) {
|
||||
if (draw.current) map?.current?.removeInteraction(draw.current)
|
||||
//if (snap.current) map?.current?.removeInteraction(snap.current)
|
||||
addInteractions(drawingLayerSource, draw, map, snap, measureDraw, measureSource, measureModify)
|
||||
@ -444,12 +526,10 @@ const MapComponent = () => {
|
||||
if (snap.current) map?.current?.removeInteraction(snap.current)
|
||||
if (measureDraw.current) map?.current?.removeInteraction(measureDraw.current)
|
||||
}
|
||||
}, [mapState.currentTool])
|
||||
}, [currentTool])
|
||||
|
||||
const [satelliteOpacity, setSatelliteOpacity] = useState<number>(0)
|
||||
|
||||
const [statusText, setStatusText] = useState('')
|
||||
|
||||
// Visibility setting
|
||||
useEffect(() => {
|
||||
satLayer.current?.setOpacity(satelliteOpacity)
|
||||
@ -468,7 +548,11 @@ const MapComponent = () => {
|
||||
|
||||
// Satellite tiles setting
|
||||
useEffect(() => {
|
||||
satLayer.current?.setSource(satMapsProvider == 'google' ? gMapsSatSource.current : satMapsProvider == 'yandex' ? yMapsSatSource.current : satMapsProvider == 'custom' ? customMapSource.current : gMapsSatSource.current)
|
||||
satLayer.current?.setSource(
|
||||
satMapsProvider == 'google' ? gMapsSatSource.current :
|
||||
satMapsProvider == 'yandex' ? yMapsSatSource.current :
|
||||
satMapsProvider == 'custom' ? customMapSource.current :
|
||||
gMapsSatSource.current)
|
||||
satLayer.current?.getSource()?.refresh()
|
||||
}, [satMapsProvider])
|
||||
|
||||
@ -517,7 +601,7 @@ const MapComponent = () => {
|
||||
if (node.shape_type === 'LINE') {
|
||||
const coordinates: Coordinate[] = []
|
||||
if (Array.isArray(node.shape)) {
|
||||
node.shape.map((point: any) => {
|
||||
node.shape.map((point: { x: number, y: number }) => {
|
||||
const coordinate = [point.x as number, point.y as number] as Coordinate
|
||||
coordinates.push(coordinate)
|
||||
})
|
||||
@ -658,24 +742,26 @@ const MapComponent = () => {
|
||||
w: 100000,
|
||||
h: 100000
|
||||
}
|
||||
let rotation = 0
|
||||
//let rotation = 0
|
||||
|
||||
if (citySettings.find(el => el.city_id === selectedCity)) {
|
||||
const settings = citySettings.find(el => el.city_id === selectedCity)
|
||||
|
||||
if (settings) {
|
||||
console.log("City settings found")
|
||||
|
||||
if (citySettings.find(el => el.city_id === selectedCity)?.scale) {
|
||||
if (settings.scale) {
|
||||
scale = {
|
||||
w: citySettings.find(el => el.city_id === selectedCity).scale,
|
||||
h: citySettings.find(el => el.city_id === selectedCity).scale
|
||||
w: settings.scale,
|
||||
h: settings.scale
|
||||
}
|
||||
}
|
||||
if (citySettings.find(el => el.city_id === selectedCity)?.offset_x && citySettings.find(el => el.city_id === selectedCity)?.offset_y) {
|
||||
offset_x = citySettings.find(el => el.city_id === selectedCity).offset_x
|
||||
offset_y = citySettings.find(el => el.city_id === selectedCity).offset_y
|
||||
if (settings.offset_x && settings.offset_y) {
|
||||
offset_x = settings.offset_x
|
||||
offset_y = settings.offset_y
|
||||
}
|
||||
|
||||
if (citySettings.find(el => el.city_id === selectedCity)?.rotation) {
|
||||
rotation = citySettings.find(el => el.city_id === selectedCity)?.rotation
|
||||
if (settings.rotation) {
|
||||
//rotation = settings.rotation
|
||||
}
|
||||
} else {
|
||||
console.log("City settings NOT found")
|
||||
@ -710,6 +796,78 @@ const MapComponent = () => {
|
||||
}
|
||||
}, [figuresData, linesData, selectedCity, selectedYear])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedCity) {
|
||||
let offset_x = 14442665.697619518
|
||||
let offset_y = 8884520.63524492
|
||||
//let scale = 9000
|
||||
//let rotation = 0
|
||||
//let image_width = 8500
|
||||
//let image_height = 12544
|
||||
|
||||
const settings = citySettings.find(el => el.city_id === selectedCity)
|
||||
|
||||
if (settings) {
|
||||
console.log("City settings found")
|
||||
|
||||
if (settings.scale) {
|
||||
//scale = settings.scale
|
||||
}
|
||||
if (settings.offset_x && settings.offset_y) {
|
||||
offset_x = settings.offset_x
|
||||
offset_y = settings.offset_y
|
||||
}
|
||||
|
||||
if (settings.image_width && settings.image_height) {
|
||||
//image_width = settings.image_width
|
||||
//image_height = settings.image_height
|
||||
}
|
||||
|
||||
if (settings.rotation) {
|
||||
//rotation = settings.rotation
|
||||
}
|
||||
} else {
|
||||
console.log("City settings NOT found")
|
||||
}
|
||||
|
||||
const imageUrl = `${import.meta.env.VITE_API_EMS_URL}/static/${selectedCity}`;
|
||||
const img = new Image();
|
||||
img.src = imageUrl;
|
||||
img.onload = () => {
|
||||
if (map.current) {
|
||||
|
||||
const width = img.naturalWidth;
|
||||
const height = img.naturalHeight;
|
||||
console.log(width, height)
|
||||
//const k = (width < height ? width / height : height / width)
|
||||
const k = 0.40340
|
||||
|
||||
console.log(k)
|
||||
|
||||
const wk = width * k
|
||||
const hk = height * k
|
||||
|
||||
const center = [offset_x + (wk), 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.current.setSource(imageSource);
|
||||
|
||||
//map.current.addLayer(imageLayer.current);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [selectedCity])
|
||||
|
||||
useEffect(() => {
|
||||
// if (map.current) {
|
||||
@ -754,7 +912,7 @@ const MapComponent = () => {
|
||||
<Flex align='center' direction='row' gap='sm'>
|
||||
<Slider w='100%' min={0} max={1} step={0.001} value={satelliteOpacity} defaultValue={satelliteOpacity} onChange={(value) => setSatelliteOpacity(Array.isArray(value) ? value[0] : value)} />
|
||||
|
||||
<MantineSelect variant='filled' value={satMapsProvider} data={[{ label: 'Google', value: 'google' }, { label: 'Yandex', value: 'yandex' }, { label: 'Custom', value: 'custom' }]} onChange={(value) => setSatMapsProvider(value as SatelliteMapsProvider)} />
|
||||
<MantineSelect variant='filled' value={satMapsProvider} data={[{ label: 'Google', value: 'google' }, { label: 'Yandex', value: 'yandex' }, { label: 'Custom', value: 'custom' }, { label: 'Static', value: 'static' }]} onChange={(value) => setSatMapsProvider(value as SatelliteMapsProvider)} />
|
||||
</Flex>
|
||||
|
||||
<form>
|
||||
@ -949,11 +1107,6 @@ const MapComponent = () => {
|
||||
|
||||
<MapStatusbar
|
||||
mapControlsStyle={mapControlsStyle}
|
||||
currentCoordinate={currentCoordinate}
|
||||
currentX={currentX}
|
||||
currentY={currentY}
|
||||
currentZ={currentZ}
|
||||
statusText={statusText}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
|
@ -1,50 +1,42 @@
|
||||
import { Divider, Flex, rem, Text } from '@mantine/core'
|
||||
import { Coordinate } from 'ol/coordinate';
|
||||
import React, { CSSProperties } from 'react'
|
||||
import { CSSProperties } from 'react'
|
||||
import { useMapStore } from '../../../store/map';
|
||||
|
||||
interface IMapStatusbarProps {
|
||||
mapControlsStyle: CSSProperties;
|
||||
currentCoordinate: Coordinate | null;
|
||||
currentX: number | undefined;
|
||||
currentY: number | undefined;
|
||||
currentZ: number | undefined;
|
||||
statusText: string;
|
||||
}
|
||||
|
||||
const MapStatusbar = ({
|
||||
mapControlsStyle,
|
||||
currentCoordinate,
|
||||
currentX,
|
||||
currentY,
|
||||
currentZ,
|
||||
statusText
|
||||
}: IMapStatusbarProps) => {
|
||||
const mapState = useMapStore()
|
||||
|
||||
return (
|
||||
<Flex gap='sm' p={'4px'} miw={'100%'} fz={'xs'} pos='absolute' bottom='0px' left='0px' style={{ ...mapControlsStyle, borderRadius: 0 }}>
|
||||
<Text fz='xs' w={rem(130)}>
|
||||
x: {currentCoordinate?.[0]}
|
||||
x: {mapState.currentCoordinate?.[0]}
|
||||
</Text>
|
||||
|
||||
<Text fz='xs' w={rem(130)}>
|
||||
y: {currentCoordinate?.[1]}
|
||||
y: {mapState.currentCoordinate?.[1]}
|
||||
</Text>
|
||||
|
||||
<Divider orientation='vertical' />
|
||||
|
||||
<Text fz='xs'>
|
||||
Z={currentZ}
|
||||
Z={mapState.currentZ}
|
||||
</Text>
|
||||
|
||||
<Text fz='xs'>
|
||||
X={currentX}
|
||||
X={mapState.currentX}
|
||||
</Text>
|
||||
|
||||
<Text fz='xs'>
|
||||
Y={currentY}
|
||||
Y={mapState.currentY}
|
||||
</Text>
|
||||
|
||||
<Text fz='xs' ml='auto'>
|
||||
{statusText}
|
||||
{mapState.statusText}
|
||||
</Text>
|
||||
</Flex>
|
||||
)
|
||||
|
@ -20,15 +20,14 @@ import ImageLayer from "ol/layer/Image";
|
||||
import { IFigure, ILine } from "../../interfaces/gis";
|
||||
import { fromCircle } from "ol/geom/Polygon";
|
||||
import { measureStyleFunction, modifyStyle } from "./Measure/MeasureStyles";
|
||||
import { getCurrentTool, getMeasureClearPrevious, getMeasureType, getTipPoint } from "../../store/map";
|
||||
import { VectorImage } from "ol/layer";
|
||||
import { getCurrentTool, getMeasureClearPrevious, getMeasureType, getTipPoint, setStatusText } from "../../store/map";
|
||||
import { MutableRefObject } from "react";
|
||||
|
||||
export function processLine(
|
||||
line: ILine,
|
||||
scaling: { w: number, h: number },
|
||||
mapCenter: Coordinate,
|
||||
linesLayer: React.MutableRefObject<VectorImage<Feature<Geometry>, VectorSource<Feature<Geometry>>>>
|
||||
linesLayer: MutableRefObject<VectorLayer<VectorSource>>
|
||||
) {
|
||||
const x1 = line.x1 * scaling.w
|
||||
const y1 = line.y1 * scaling.h
|
||||
@ -55,7 +54,7 @@ export function processFigure(
|
||||
figure: IFigure,
|
||||
scaling: { w: number, h: number },
|
||||
mapCenter: Coordinate,
|
||||
figuresLayer: React.MutableRefObject<VectorImage<Feature<Geometry>, VectorSource<Feature<Geometry>>>>
|
||||
figuresLayer: MutableRefObject<VectorLayer<VectorSource>>
|
||||
) {
|
||||
if (figure.figure_type_id == 1) {
|
||||
const width = figure.width * scaling.w
|
||||
@ -262,8 +261,6 @@ export function regionsInit(
|
||||
map: React.MutableRefObject<Map | null>,
|
||||
selectedRegion: React.MutableRefObject<Feature<Geometry> | null>,
|
||||
regionsLayer: React.MutableRefObject<VectorImageLayer<Feature<Geometry>, VectorSource<Feature<Geometry>>>>,
|
||||
setStatusText: (value: React.SetStateAction<string>) => void,
|
||||
|
||||
) {
|
||||
map.current?.on('click', function (e) {
|
||||
if (selectedRegion.current !== null) {
|
||||
|
Reference in New Issue
Block a user