forked from VinokurovVE/tests
Update
This commit is contained in:
@ -4,17 +4,15 @@ import Map from 'ol/Map'
|
||||
import View from 'ol/View'
|
||||
import { Draw, Modify, Select, Snap, Translate } from 'ol/interaction'
|
||||
import { ImageStatic, OSM, Vector as VectorSource, XYZ } from 'ol/source'
|
||||
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
|
||||
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 { drawingLayerStyle, highlightStyleRed, highlightStyleYellow, overlayStyle, regionsLayerStyle } from './MapStyles'
|
||||
import { googleMapsSatelliteSource, regionsLayerSource, yandexMapsSatelliteSource } from './MapSources'
|
||||
import { mapCenter } from './MapConstants'
|
||||
import ImageLayer from 'ol/layer/Image'
|
||||
import VectorImageLayer from 'ol/layer/VectorImage'
|
||||
import { LineString, Point } from 'ol/geom'
|
||||
import { LineString, Point, SimpleGeometry } from 'ol/geom'
|
||||
import { fromExtent } from 'ol/geom/Polygon'
|
||||
import Collection from 'ol/Collection'
|
||||
import { Coordinate } from 'ol/coordinate'
|
||||
@ -24,22 +22,37 @@ 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, Text as MantineText, MantineStyleProp, rem, ScrollAreaAutosize, Slider, useMantineColorScheme, Portal, Tree, Group, TreeNodeData, Button, useTree, Timeline, Text, Stack, Overlay } from '@mantine/core'
|
||||
import { IconChevronDown, IconPlus, IconSettings, IconTable, IconUpload } from '@tabler/icons-react'
|
||||
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 { IconPlus, IconSearch, IconSettings, IconTable, IconUpload } from '@tabler/icons-react'
|
||||
import { getGridCellPosition } from './mapUtils'
|
||||
import { IFigure, ILine } from '../../interfaces/gis'
|
||||
import axios from 'axios'
|
||||
import ObjectParameter from './ObjectParameter'
|
||||
import { IObjectData, IObjectList, IObjectParam } from '../../interfaces/objects'
|
||||
import ObjectData from './ObjectData'
|
||||
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 { MapTreeCheckbox } from './MapTree/MapTreeCheckbox'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useThrottle } from '@uidotdev/usehooks'
|
||||
import ObjectTree from '../Tree/ObjectTree'
|
||||
import { setCurrentObjectId, setSelectedCity, setSelectedYear, useObjectsStore } from '../../store/objects'
|
||||
|
||||
// Settings for cities
|
||||
const citySettings = [
|
||||
{
|
||||
city_id: 145,
|
||||
// scale: 10000,
|
||||
scale: 9000,
|
||||
offset_x: 14442665.697619518,
|
||||
offset_y: 8884520.63524492,
|
||||
rotation: 0
|
||||
}
|
||||
]
|
||||
|
||||
const MapComponent = () => {
|
||||
const { selectedCity, selectedYear, currentObjectId } = useObjectsStore()
|
||||
|
||||
const mapState = useMapStore()
|
||||
|
||||
const [currentCoordinate, setCurrentCoordinate] = useState<Coordinate | null>(null)
|
||||
@ -121,24 +134,27 @@ const MapComponent = () => {
|
||||
}
|
||||
}))
|
||||
|
||||
const figuresLayer = useRef<VectorLayer>(new VectorLayer({
|
||||
const figuresLayer = useRef<VectorImage>(new VectorImage({
|
||||
source: new VectorSource(),
|
||||
declutter: true,
|
||||
properties: {
|
||||
id: uuidv4(),
|
||||
name: 'Фигуры'
|
||||
}
|
||||
}))
|
||||
|
||||
const linesLayer = useRef<VectorLayer>(new VectorLayer({
|
||||
const linesLayer = useRef<VectorImage>(new VectorImage({
|
||||
source: new VectorSource(),
|
||||
declutter: true,
|
||||
properties: {
|
||||
id: uuidv4(),
|
||||
name: 'Линии'
|
||||
}
|
||||
}))
|
||||
|
||||
const regionsLayer = useRef<VectorImageLayer>(new VectorImageLayer({
|
||||
const regionsLayer = useRef<VectorImage>(new VectorImage({
|
||||
source: regionsLayerSource,
|
||||
declutter: true,
|
||||
style: regionsLayerStyle,
|
||||
properties: {
|
||||
id: uuidv4(),
|
||||
@ -356,14 +372,14 @@ const MapComponent = () => {
|
||||
],
|
||||
target: mapElement.current as HTMLDivElement,
|
||||
view: new View({
|
||||
center: transform([129.7659541, 62.009504], 'EPSG:4326', 'EPSG:3857'),//center: fromLonLat([130.401113, 67.797368]),
|
||||
zoom: 17,
|
||||
center: transform([129.7466541, 62.083504], 'EPSG:4326', 'EPSG:3857'),//center: fromLonLat([130.401113, 67.797368]),
|
||||
zoom: 16,
|
||||
maxZoom: 21,
|
||||
//extent: mapExtent,
|
||||
}),
|
||||
})
|
||||
|
||||
map.current.on('pointermove', function (e: MapBrowserEvent<any>) {
|
||||
map.current.on('pointermove', function (e: MapBrowserEvent<UIEvent>) {
|
||||
setCurrentCoordinate(e.coordinate)
|
||||
const currentExtent = get('EPSG:3857')?.getExtent() as Extent
|
||||
const { tileX, tileY } = getGridCellPosition(e.coordinate[0], e.coordinate[1], currentExtent, Number(map.current?.getView().getZoom()?.toFixed(0)))
|
||||
@ -372,7 +388,7 @@ const MapComponent = () => {
|
||||
setCurrentY(tileY)
|
||||
})
|
||||
|
||||
map.current.on('click', function (e: MapBrowserEvent<any>) {
|
||||
map.current.on('click', function (e: MapBrowserEvent<UIEvent>) {
|
||||
const pixel = map.current?.getEventPixel(e.originalEvent)
|
||||
|
||||
if (pixel) {
|
||||
@ -430,7 +446,7 @@ const MapComponent = () => {
|
||||
}
|
||||
}, [mapState.currentTool])
|
||||
|
||||
const [satelliteOpacity, setSatelliteOpacity] = useState<number>(1)
|
||||
const [satelliteOpacity, setSatelliteOpacity] = useState<number>(0)
|
||||
|
||||
const [statusText, setStatusText] = useState('')
|
||||
|
||||
@ -513,31 +529,14 @@ const MapComponent = () => {
|
||||
}
|
||||
}, [nodes])
|
||||
|
||||
const [currentObjectId, setCurrentObjectId] = useState<string | null>(null)
|
||||
|
||||
const [selectedCity, setSelectedCity] = useState<number | null>(null)
|
||||
const [selectedYear, setSelectedYear] = useState<number | null>(2023)
|
||||
const [citiesPage, setCitiesPage] = useState<number>(0)
|
||||
|
||||
const [searchCity, setSearchCity] = useState<string | undefined>("")
|
||||
const throttledSearchCity = useThrottle(searchCity, 500);
|
||||
|
||||
const { data: existingObjectsList } = useSWR(
|
||||
selectedYear && selectedCity ? `/general/objects/list?year=${selectedYear}&city_id=${selectedCity}&planning=0` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
}
|
||||
)
|
||||
const [searchObject, setSearchObject] = useState<string | undefined>("")
|
||||
const throttledSearchObject = useThrottle(searchObject, 500);
|
||||
|
||||
const { data: planningObjectsList } = useSWR(
|
||||
selectedYear && selectedCity ? `/general/objects/list?year=${selectedYear}&city_id=${selectedCity}&planning=1` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
}
|
||||
)
|
||||
|
||||
const [objectsList, setObjectsList] = useState<TreeNodeData[] | null>(null)
|
||||
const [selectedObjectList, setSelectedObjectList] = useState<number | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
@ -549,7 +548,7 @@ const MapComponent = () => {
|
||||
if (selectedObjectList == feature.get('type')) {
|
||||
feature.setStyle(highlightStyleYellow);
|
||||
} else {
|
||||
feature.setStyle(null); // Reset to default style
|
||||
feature.setStyle(undefined); // Reset to default style
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -560,81 +559,70 @@ const MapComponent = () => {
|
||||
if (selectedObjectList == feature.get('type')) {
|
||||
feature.setStyle(highlightStyleYellow);
|
||||
} else {
|
||||
feature.setStyle(null); // Reset to default style
|
||||
feature.setStyle(undefined); // Reset to default style
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [selectedObjectList])
|
||||
|
||||
useEffect(() => {
|
||||
if (existingObjectsList && planningObjectsList) {
|
||||
setObjectsList([
|
||||
{
|
||||
label: 'Существующие',
|
||||
value: 'existing',
|
||||
children: existingObjectsList.map((list: IObjectList) => ({
|
||||
label:list.name,
|
||||
count: list.count,
|
||||
value: list.id,
|
||||
})),
|
||||
},
|
||||
{
|
||||
label: 'Планируемые',
|
||||
value: 'planning',
|
||||
children: planningObjectsList.map((list: IObjectList) => ({
|
||||
label: list.name,
|
||||
count: list.count,
|
||||
value: list.id
|
||||
}))
|
||||
}
|
||||
])
|
||||
}
|
||||
}, [existingObjectsList, planningObjectsList])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentObjectId) {
|
||||
if (figuresLayer.current) {
|
||||
// Reset styles and apply highlight to matching features
|
||||
figuresLayer.current.getSource()?.getFeatures().forEach((feature) => {
|
||||
figuresLayer.current.getSource()?.getFeatures().forEach((feature: Feature) => {
|
||||
if (currentObjectId == feature.get('object_id')) {
|
||||
feature.setStyle(highlightStyleRed);
|
||||
|
||||
const geometry = feature.getGeometry()
|
||||
|
||||
if (geometry) {
|
||||
map.current?.getView().fit(geometry as SimpleGeometry, { duration: 500, maxZoom: 18 })
|
||||
//map.current?.getView().animate({ center: calculateCenter(geometry as SimpleGeometry).center })
|
||||
}
|
||||
|
||||
} else {
|
||||
feature.setStyle(null); // Reset to default style
|
||||
feature.setStyle(undefined); // Reset to default style
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (linesLayer.current) {
|
||||
// Reset styles and apply highlight to matching features
|
||||
linesLayer.current.getSource()?.getFeatures().forEach((feature) => {
|
||||
linesLayer.current.getSource()?.getFeatures().forEach((feature: Feature) => {
|
||||
if (currentObjectId == feature.get('object_id')) {
|
||||
feature.setStyle(highlightStyleRed);
|
||||
|
||||
const geometry = feature.getGeometry()
|
||||
if (geometry) {
|
||||
map.current?.getView().fit(geometry as SimpleGeometry, { duration: 500, maxZoom: 18 })
|
||||
}
|
||||
} else {
|
||||
feature.setStyle(null); // Reset to default style
|
||||
feature.setStyle(undefined); // Reset to default style
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [currentObjectId])
|
||||
|
||||
const { data: currentObjectData } = useSWR(
|
||||
currentObjectId ? `/general/objects/${currentObjectId}` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
}
|
||||
)
|
||||
|
||||
const { data: valuesData } = useSWR(
|
||||
currentObjectId ? `/general/values/all?object_id=${currentObjectId}` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
revalidateOnFocus: false,
|
||||
revalidateIfStale: false
|
||||
}
|
||||
)
|
||||
|
||||
const { data: citiesData } = useSWR(
|
||||
`/general/cities/all?limit=${10}&offset=${citiesPage || 0}${searchCity ? `&search=${searchCity}` : ''}`,
|
||||
`/general/cities/all?limit=${10}&offset=${citiesPage || 0}${throttledSearchCity ? `&search=${throttledSearchCity}` : ''}`,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
}
|
||||
)
|
||||
|
||||
const { data: searchData } = useSWR(
|
||||
throttledSearchObject !== "" && selectedCity && selectedYear ? `/general/search/objects?q=${throttledSearchObject}&id_city=${selectedCity}&year=${selectedYear}` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
@ -664,16 +652,45 @@ const MapComponent = () => {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
let offset_x = 14444582.697619518
|
||||
let offset_y = 8866450.63524492
|
||||
let scale = {
|
||||
w: 100000,
|
||||
h: 100000
|
||||
}
|
||||
let rotation = 0
|
||||
|
||||
if (citySettings.find(el => el.city_id === selectedCity)) {
|
||||
console.log("City settings found")
|
||||
|
||||
if (citySettings.find(el => el.city_id === selectedCity)?.scale) {
|
||||
scale = {
|
||||
w: citySettings.find(el => el.city_id === selectedCity).scale,
|
||||
h: citySettings.find(el => el.city_id === selectedCity).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 (citySettings.find(el => el.city_id === selectedCity)?.rotation) {
|
||||
rotation = citySettings.find(el => el.city_id === selectedCity)?.rotation
|
||||
}
|
||||
} else {
|
||||
console.log("City settings NOT found")
|
||||
}
|
||||
|
||||
if (Array.isArray(figuresData)) {
|
||||
figuresLayer.current.getSource()?.clear()
|
||||
if (figuresData.length > 0) {
|
||||
const scaling = {
|
||||
w: 10000, // responseData[0].width
|
||||
h: 10000 // responseData[0].width
|
||||
w: scale.w, // responseData[0].width
|
||||
h: scale.h // responseData[0].width
|
||||
}
|
||||
|
||||
figuresData.map((figure: IFigure) => {
|
||||
processFigure(figure, scaling, mapCenter, figuresLayer)
|
||||
processFigure(figure, scaling, [offset_x, offset_y], figuresLayer)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -682,17 +699,54 @@ const MapComponent = () => {
|
||||
linesLayer.current.getSource()?.clear()
|
||||
if (linesData.length > 0) {
|
||||
const scaling = {
|
||||
w: 10000, // responseData[0].width
|
||||
h: 10000 // responseData[0].width
|
||||
w: scale.w, // responseData[0].width
|
||||
h: scale.h // responseData[0].width
|
||||
}
|
||||
|
||||
linesData.map((line: ILine) => {
|
||||
processLine(line, scaling, mapCenter, linesLayer)
|
||||
processLine(line, scaling, [offset_x, offset_y], linesLayer)
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [figuresData, linesData, selectedCity, selectedYear])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// if (map.current) {
|
||||
// map.current.on('postcompose', function () {
|
||||
// if (colorScheme === 'dark') {
|
||||
// document.querySelector('canvas').style.filter = 'grayscale(80%) invert(100%) hue-rotate(180deg)'
|
||||
// } else {
|
||||
// document.querySelector('canvas').style.filter = 'grayscale(0%) invert(0%) hue-rotate(0deg)'
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
if (baseLayer.current) {
|
||||
baseLayer.current.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';
|
||||
}
|
||||
} else {
|
||||
if (e.context) {
|
||||
const context = e.context as CanvasRenderingContext2D;
|
||||
context.filter = 'none';
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
baseLayer.current.on('postrender', function (e) {
|
||||
if (e.context) {
|
||||
const context = e.context as CanvasRenderingContext2D;
|
||||
context.filter = 'none';
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [colorScheme])
|
||||
|
||||
return (
|
||||
<Box w={'100%'} h='100%' pos={'relative'}>
|
||||
<Portal target='#header-portal'>
|
||||
@ -703,11 +757,37 @@ const MapComponent = () => {
|
||||
<MantineSelect variant='filled' value={satMapsProvider} data={[{ label: 'Google', value: 'google' }, { label: 'Yandex', value: 'yandex' }, { label: 'Custom', value: 'custom' }]} onChange={(value) => setSatMapsProvider(value as SatelliteMapsProvider)} />
|
||||
</Flex>
|
||||
|
||||
<form>
|
||||
<Autocomplete
|
||||
placeholder="Поиск"
|
||||
flex={'1'}
|
||||
data={searchData ? searchData.map((item: { value: string, id_object: string }) => ({ label: item.value, value: item.id_object.toString() })) : []}
|
||||
//onSelect={(e) => console.log(e.currentTarget.value)}
|
||||
onChange={(value) => setSearchObject(value)}
|
||||
onOptionSubmit={(value) => setCurrentObjectId(value)}
|
||||
rightSection={
|
||||
searchObject !== '' && (
|
||||
<CloseButton
|
||||
size="sm"
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={() => {
|
||||
setSearchObject('')
|
||||
//setSelectedCity(null)
|
||||
}}
|
||||
aria-label="Clear value"
|
||||
/>
|
||||
)
|
||||
}
|
||||
leftSection={<IconSearch size={16} />}
|
||||
value={searchObject}
|
||||
/>
|
||||
</form>
|
||||
|
||||
<form>
|
||||
<Autocomplete
|
||||
placeholder="Район"
|
||||
flex={'1'}
|
||||
data={citiesData ? citiesData.map((item: any) => ({ label: item.name, value: item.id.toString() })) : []}
|
||||
data={citiesData ? citiesData.map((item: { name: string, id: number }) => ({ label: item.name, value: item.id.toString() })) : []}
|
||||
//onSelect={(e) => console.log(e.currentTarget.value)}
|
||||
onChange={(value) => setSearchCity(value)}
|
||||
onOptionSubmit={(value) => setSelectedCity(Number(value))}
|
||||
@ -729,20 +809,12 @@ const MapComponent = () => {
|
||||
</form>
|
||||
|
||||
<MantineSelect
|
||||
data={[
|
||||
{ label: '2018', value: '2018' },
|
||||
{ label: '2019', value: '2019' },
|
||||
{ label: '2020', value: '2020' },
|
||||
{ label: '2021', value: '2021' },
|
||||
{ label: '2022', value: '2022' },
|
||||
{ label: '2023', value: '2023' },
|
||||
{ label: '2024', value: '2024' },
|
||||
]}
|
||||
w='84px'
|
||||
data={['2018', '2019', '2020', '2021', '2022', '2023', '2024'].map(el => ({ label: el, value: el }))}
|
||||
onChange={(e) => {
|
||||
setSelectedYear(Number(e))
|
||||
}}
|
||||
defaultValue={selectedYear?.toString()}
|
||||
//variant="unstyled"
|
||||
allowDeselect={false}
|
||||
/>
|
||||
|
||||
@ -766,7 +838,7 @@ const MapComponent = () => {
|
||||
|
||||
<Flex direction='column' mah={'86%'} pl='sm' style={{
|
||||
...mapControlsStyle,
|
||||
maxWidth: '300px',
|
||||
maxWidth: '340px',
|
||||
width: '100%',
|
||||
top: '8px',
|
||||
left: '8px',
|
||||
@ -791,165 +863,87 @@ const MapComponent = () => {
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
|
||||
<Accordion variant='filled' style={{ backgroundColor: 'transparent' }} defaultValue='Объекты'>
|
||||
<Accordion.Item key={'objects'} value={'Объекты'}>
|
||||
<Accordion.Control icon={<IconTable />}>{'Объекты'}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
{objectsList &&
|
||||
<Tree
|
||||
data={objectsList}
|
||||
selectOnClick
|
||||
levelOffset={23}
|
||||
renderNode={({ node, expanded, hasChildren, elementProps }) => (
|
||||
<Group gap={6} {...elementProps} onClick={async (e) => {
|
||||
elementProps.onClick(e)
|
||||
|
||||
if (node.value !== 'existing' && node.value !== 'planning') {
|
||||
setSelectedObjectList(Number(node.value))
|
||||
|
||||
try {
|
||||
// Fetch data from the API based on the node's value
|
||||
await fetcher(`/general/objects/list?type=${node.value}&city_id=${selectedCity}&year=${selectedYear}&planning=0`, BASE_URL.ems).then(res => {
|
||||
setObjectsList((prevList) => {
|
||||
return prevList.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
children: [
|
||||
...(item.children.map(child => {
|
||||
if (child.value == node.value) {
|
||||
return {
|
||||
...child,
|
||||
children: res.map(object => {
|
||||
return {
|
||||
label: object.object_id,
|
||||
value: object.object_id
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return { ...child }
|
||||
}
|
||||
}) || []),
|
||||
],
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
})
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{(hasChildren || node?.count) && (
|
||||
<IconChevronDown
|
||||
size={18}
|
||||
style={{ transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' }}
|
||||
/>
|
||||
)}
|
||||
<MantineText size='sm'>{`${node.label} ${node?.count ? `(${node.count})` : ''}`}</MantineText>
|
||||
</Group>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
{/* {currentObjectId &&
|
||||
<Accordion.Item key={'current_object'} value={currentObjectId}>
|
||||
<Accordion.Control icon={<IconTable />}>
|
||||
{'Текущий объект'}
|
||||
</Accordion.Control>
|
||||
{selectedCity &&
|
||||
<Accordion variant='filled' style={{ backgroundColor: 'transparent' }} defaultValue='Объекты'>
|
||||
<Accordion.Item key={'objects'} value={'Объекты'}>
|
||||
<Accordion.Control icon={<IconTable />}>{'Объекты'}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<ObjectData {...currentObjectData as IObjectData} />
|
||||
<ObjectTree />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
} */}
|
||||
|
||||
{valuesData &&
|
||||
<Accordion.Item key={'parameters'} value={'Параметры объекта'}>
|
||||
<Accordion.Control icon={<IconTable />}>{'Параметры объекта'}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Flex gap={'sm'} direction={'column'}>
|
||||
{Array.isArray(valuesData) &&
|
||||
// Group objects by `id_param`
|
||||
Object.entries(
|
||||
valuesData.reduce((acc, param) => {
|
||||
if (!acc[param.id_param]) {
|
||||
acc[param.id_param] = [];
|
||||
}
|
||||
acc[param.id_param].push(param);
|
||||
return acc;
|
||||
}, {} as Record<string, IObjectParam[]>)
|
||||
).map(([id_param, params]) => {
|
||||
// Step 1: Sort the parameters by date_s (start date) and date_po (end date)
|
||||
const sortedParams = params.sort((b, a) => {
|
||||
const dateA = new Date(a.date_s || 0);
|
||||
const dateB = new Date(b.date_s || 0);
|
||||
return dateA.getTime() - dateB.getTime();
|
||||
});
|
||||
{valuesData &&
|
||||
<Accordion.Item key={'parameters'} value={'Параметры объекта'}>
|
||||
<Accordion.Control icon={<IconTable />}>{'Параметры объекта'}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Flex gap={'sm'} direction={'column'}>
|
||||
{Array.isArray(valuesData) &&
|
||||
Object.entries(
|
||||
valuesData.reduce((acc, param) => {
|
||||
if (!acc[param.id_param]) {
|
||||
acc[param.id_param] = [];
|
||||
}
|
||||
acc[param.id_param].push(param);
|
||||
return acc;
|
||||
}, {} as Record<string, IObjectParam[]>)
|
||||
).map(([id_param, params]) => {
|
||||
// Step 1: Sort the parameters by date_s (start date) and date_po (end date)
|
||||
const sortedParams = (params as IObjectParam[]).sort((b, a) => {
|
||||
const dateA = new Date(a.date_s || 0);
|
||||
const dateB = new Date(b.date_s || 0);
|
||||
return dateA.getTime() - dateB.getTime();
|
||||
});
|
||||
|
||||
return sortedParams.length > 1 ? (
|
||||
// Step 2: Render Mantine Timeline for multiple entries with the same `id_param`
|
||||
<Timeline
|
||||
key={id_param}
|
||||
active={0}
|
||||
bulletSize={18}
|
||||
>
|
||||
{sortedParams.map((param, index) => (
|
||||
<Timeline.Item
|
||||
key={index}
|
||||
style={{
|
||||
filter: index !== 0 ? 'grayscale(100%) opacity(50%)' : 'none'
|
||||
}}
|
||||
>
|
||||
<ObjectParameter param={param} showLabel={false} />
|
||||
<Text size='xs'>
|
||||
{new Date(param.date_s).toLocaleDateString('en-GB').split('/').join('.')} - {new Date(param.date_po).toLocaleDateString('en-GB') === '01/01/1970' ? 'По сей день' : new Date(param.date_po).toLocaleDateString('en-GB').split('/').join('.')}
|
||||
</Text>
|
||||
</Timeline.Item>
|
||||
))}
|
||||
</Timeline>
|
||||
) : (
|
||||
// Step 3: Render ObjectParameter directly if there's only one entry
|
||||
<ObjectParameter key={id_param} param={sortedParams[0]} />
|
||||
);
|
||||
})
|
||||
}
|
||||
</Flex>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
}
|
||||
</Accordion>
|
||||
return sortedParams.length > 1 ? (
|
||||
// Step 2: Render Mantine Timeline for multiple entries with the same `id_param`
|
||||
<Timeline
|
||||
key={id_param}
|
||||
active={0}
|
||||
bulletSize={18}
|
||||
>
|
||||
{sortedParams.map((param: IObjectParam, index: number) => (
|
||||
<Timeline.Item
|
||||
key={index}
|
||||
style={{
|
||||
filter: index !== 0 ? 'grayscale(100%) opacity(50%)' : 'none'
|
||||
}}
|
||||
>
|
||||
<ObjectParameter param={param} showLabel={false} />
|
||||
<Text size='xs'>
|
||||
{new Date(param.date_s).toLocaleDateString('en-GB').split('/').join('.')} - {new Date(param.date_po).toLocaleDateString('en-GB') === '01/01/1970' ? 'По сей день' : new Date(param.date_po).toLocaleDateString('en-GB').split('/').join('.')}
|
||||
</Text>
|
||||
</Timeline.Item>
|
||||
))}
|
||||
</Timeline>
|
||||
) : (
|
||||
<ObjectParameter key={id_param} param={sortedParams[0]} />
|
||||
);
|
||||
})
|
||||
}
|
||||
</Flex>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
}
|
||||
</Accordion>
|
||||
}
|
||||
|
||||
<Accordion variant='filled' style={{ backgroundColor: 'transparent' }} defaultValue='Слои'>
|
||||
<Accordion.Item key={'objects'} value={'Слои'}>
|
||||
<Accordion.Control icon={<IconTable />}>{'Слои'}</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
{map.current?.getLayers().getArray() &&
|
||||
<Tree
|
||||
data={map.current?.getLayers().getArray().map(layer => {
|
||||
return {
|
||||
label: layer.get('name'),
|
||||
value: layer.get('id')
|
||||
}
|
||||
}) as TreeNodeData[]}
|
||||
//selectOnClick
|
||||
expandOnClick={false}
|
||||
levelOffset={23}
|
||||
renderNode={MapTreeCheckbox}
|
||||
/>
|
||||
}
|
||||
|
||||
{map.current?.getLayers().getArray() && map.current?.getLayers().getArray().map((layer, index) => (
|
||||
<Flex key={`layer-${index}`} gap='xs' align='center'>
|
||||
<Checkbox.Indicator
|
||||
checked={layer.getLayerState().visible}
|
||||
onClick={() => layer.getLayerState().visible ? layer.setVisible(false) : layer.setVisible(true)}
|
||||
/>
|
||||
<NavLink p={0} label={layer.get('name')} onClick={() => { console.log(layer.getLayerState()) }} />
|
||||
</Flex>
|
||||
))}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
</Stack>
|
||||
|
||||
</ScrollAreaAutosize>
|
||||
</Flex>
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Checkbox, Group, RenderTreeNodePayload } from "@mantine/core";
|
||||
import { Checkbox, Group, RenderTreeNodePayload, Text } from "@mantine/core";
|
||||
import { IconChevronDown } from "@tabler/icons-react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const MapTreeCheckbox = ({
|
||||
node,
|
||||
@ -12,10 +11,6 @@ export const MapTreeCheckbox = ({
|
||||
const checked = tree.isNodeChecked(node.value);
|
||||
const indeterminate = tree.isNodeIndeterminate(node.value);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(node.value)
|
||||
}, [checked])
|
||||
|
||||
return (
|
||||
<Group gap="xs" {...elementProps}>
|
||||
<Checkbox.Indicator
|
||||
@ -25,7 +20,7 @@ export const MapTreeCheckbox = ({
|
||||
/>
|
||||
|
||||
<Group gap={5} onClick={() => tree.toggleExpanded(node.value)}>
|
||||
<span>{node.label}</span>
|
||||
<Text size="xs">{node.label}</Text>
|
||||
|
||||
{hasChildren && (
|
||||
<IconChevronDown
|
||||
|
@ -1,10 +1,46 @@
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from '../../http/axiosInstance'
|
||||
import { BASE_URL } from '../../constants'
|
||||
import { Checkbox, Divider, Flex, Grid, Stack, Text } from '@mantine/core'
|
||||
import { IObjectParam, IParam } from '../../interfaces/objects'
|
||||
import { decodeDoubleEncodedString } from '../../utils/format'
|
||||
import TCBParameter from './TCBParameter'
|
||||
import TableValue from './TableValue'
|
||||
|
||||
export function formatNumericValue(format: string, value: string) {
|
||||
// Extract precision and scale from the format string
|
||||
const regex = /numeric\((\d+),(\d+)\)/;
|
||||
const match = format.match(regex);
|
||||
if (!match) return value; // return original if format is not correct
|
||||
|
||||
const precision = parseInt(match[1], 10); // Total number of digits
|
||||
const scale = parseInt(match[2], 10); // Number of digits after the decimal point
|
||||
|
||||
// Convert value to a number and handle cases like empty value or invalid input
|
||||
const numericValue = parseFloat(value);
|
||||
|
||||
if (isNaN(numericValue)) {
|
||||
return '0'.padStart(precision - scale, '0') + '.' + '0'.repeat(scale); // fallback in case of invalid value
|
||||
}
|
||||
|
||||
// Ensure the value has the correct number of digits after the decimal point (scale)
|
||||
let formattedValue = numericValue.toFixed(scale);
|
||||
|
||||
// Ensure the total length doesn't exceed the precision
|
||||
const totalDigits = formattedValue.replace('.', '').length;
|
||||
|
||||
if (totalDigits > precision) {
|
||||
// Truncate the value if it exceeds the total precision
|
||||
formattedValue = numericValue.toPrecision(precision);
|
||||
}
|
||||
|
||||
// Pad with leading zeros if necessary (if it's an integer and the precision is greater than the scale)
|
||||
const [integerPart, decimalPart] = formattedValue.split('.');
|
||||
|
||||
// Ensure the integer part doesn't exceed the precision
|
||||
const paddedInteger = integerPart.padStart(precision - scale, '0');
|
||||
|
||||
// Reassemble the number
|
||||
return `${paddedInteger}.${decimalPart.padEnd(scale, '0')}`;
|
||||
}
|
||||
|
||||
interface ObjectParameterProps {
|
||||
showLabel?: boolean,
|
||||
@ -12,56 +48,61 @@ interface ObjectParameterProps {
|
||||
}
|
||||
|
||||
const ObjectParameter = ({
|
||||
param,
|
||||
showLabel = true
|
||||
param
|
||||
}: ObjectParameterProps) => {
|
||||
const { data: paramData } = useSWR(
|
||||
`/general/params/all?param_id=${param.id_param}`,
|
||||
(url) => fetcher(url, BASE_URL.ems).then(res => res[0] as IParam),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
revalidateOnFocus: false,
|
||||
revalidateIfStale: false
|
||||
}
|
||||
)
|
||||
|
||||
const Parameter = (type: string, name: string, value: unknown, vtable: string) => {
|
||||
const Parameter = (type: string, name: string, value: unknown, vtable: string, unit: string | null) => {
|
||||
switch (type) {
|
||||
case 'bit':
|
||||
return (
|
||||
<Flex direction='row' align='center' gap='sm'>
|
||||
<Checkbox defaultChecked={value as boolean} />
|
||||
<Text>{name}</Text>
|
||||
</Flex>
|
||||
)
|
||||
case 'varchar(200)':
|
||||
return (
|
||||
<Text>
|
||||
{decodeDoubleEncodedString(value as string)}
|
||||
</Text>
|
||||
)
|
||||
case 'varchar(5)':
|
||||
return (
|
||||
<Text>
|
||||
{decodeDoubleEncodedString(value as string)}
|
||||
</Text>
|
||||
<TableValue value={value} name={name} type='boolean' />
|
||||
)
|
||||
case 'bigint':
|
||||
return (
|
||||
<Text>
|
||||
{(value as string)}
|
||||
</Text>
|
||||
<TableValue value={value} name={name} type='number' />
|
||||
)
|
||||
case 'tinyint':
|
||||
return (
|
||||
<TableValue value={value} name={name} type='number' />
|
||||
)
|
||||
// TODO: Calculate from calc procedures
|
||||
case 'calculate':
|
||||
return (
|
||||
<TableValue value={value} name={name} type='value' />
|
||||
)
|
||||
case 'GTCB':
|
||||
return (
|
||||
<TCBParameter value={value as string} vtable={vtable} />
|
||||
<TCBParameter value={value as string} vtable={vtable} name={name} />
|
||||
)
|
||||
case 'TCB':
|
||||
return (
|
||||
<TCBParameter value={value as string} vtable={vtable} />
|
||||
<TCBParameter value={value as string} vtable={vtable} name={name} />
|
||||
)
|
||||
case type.match(/varchar\((\d+)\)/)?.input:
|
||||
return (
|
||||
<TableValue value={value} name={name} type='string' />
|
||||
)
|
||||
case type.match(/numeric\((\d+),(\d+)\)/)?.input:
|
||||
return (
|
||||
<TableValue value={value} name={name} type='number' unit={unit} />
|
||||
)
|
||||
case 'year':
|
||||
return (
|
||||
<TableValue value={value} name={name} type='number' />
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<div>
|
||||
{type}
|
||||
{value as string}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -70,12 +111,7 @@ const ObjectParameter = ({
|
||||
return (
|
||||
<>
|
||||
{paramData &&
|
||||
<Stack gap={0}>
|
||||
{showLabel &&
|
||||
<Divider my="xs" label={paramData.name} labelPosition="left" />
|
||||
}
|
||||
{Parameter(paramData.format, paramData.name, param.value, paramData.vtable)}
|
||||
</Stack>
|
||||
Parameter(paramData.format, paramData.name, param.value, paramData.vtable, paramData.unit)
|
||||
}
|
||||
</>
|
||||
)
|
||||
|
@ -1,30 +1,20 @@
|
||||
import React from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from '../../http/axiosInstance'
|
||||
import { BASE_URL } from '../../constants'
|
||||
import { Text } from '@mantine/core'
|
||||
import TableValue from './TableValue'
|
||||
|
||||
interface ITCBParameterProps {
|
||||
value: string,
|
||||
vtable: string,
|
||||
inactive?: boolean
|
||||
}
|
||||
|
||||
interface vStreet {
|
||||
id: number,
|
||||
id_city: number,
|
||||
name: string,
|
||||
kv: number
|
||||
}
|
||||
|
||||
interface tType {
|
||||
id: number,
|
||||
name: string,
|
||||
inactive?: boolean,
|
||||
name: string
|
||||
}
|
||||
|
||||
const TCBParameter = ({
|
||||
value,
|
||||
vtable
|
||||
vtable,
|
||||
name
|
||||
}: ITCBParameterProps) => {
|
||||
|
||||
//Get value
|
||||
@ -32,16 +22,8 @@ const TCBParameter = ({
|
||||
`/general/params/tcb?id=${value}&vtable=${vtable}`,
|
||||
(url) => fetcher(url, BASE_URL.ems).then(res => res[0]),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
}
|
||||
)
|
||||
|
||||
//Get available values
|
||||
const { data: tcbAll } = useSWR(
|
||||
`/general/params/tcb?vtable=${vtable}`,
|
||||
(url) => fetcher(url, BASE_URL.ems).then(res => res),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
revalidateOnFocus: false,
|
||||
revalidateIfStale: false
|
||||
}
|
||||
)
|
||||
|
||||
@ -49,43 +31,84 @@ const TCBParameter = ({
|
||||
switch (vtable) {
|
||||
case 'vStreets':
|
||||
return (
|
||||
<Text>
|
||||
{JSON.stringify(tcbValue)}
|
||||
</Text>
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'tTypes':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue as tType)?.name}
|
||||
</Text>
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vPipesGround':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue)?.name}
|
||||
</Text>
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vRepairEvent':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue)?.name}
|
||||
</Text>
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vPipesMaterial':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue)?.name}
|
||||
</Text>
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vBoilers':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue)?.name}
|
||||
</Text>
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vHotWaterTypes':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vHeatingTypes':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vColdWaterTypes':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vCanalization':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vElectroSupplyTypes':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vGasSupplyTypes':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vFoundation':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vMaterialsWall':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vCovering':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vRoof':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vTechStatus':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vPipeOutDiameters':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
case 'vPipeDiameters':
|
||||
return (
|
||||
<TableValue value={tcbValue?.id} name={name} type='select' vtable={vtable} />
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<Text>
|
||||
{JSON.stringify(name)}
|
||||
{JSON.stringify(tcbValue)}
|
||||
</Text>
|
||||
)
|
||||
|
71
client/src/components/map/TableValue.tsx
Normal file
71
client/src/components/map/TableValue.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import { Checkbox, ComboboxData, Grid, NumberInput, Select, Text, Textarea } from '@mantine/core';
|
||||
import useSWR from 'swr';
|
||||
import { fetcher } from '../../http/axiosInstance';
|
||||
import { BASE_URL } from '../../constants';
|
||||
import { useObjectsStore } from '../../store/objects';
|
||||
|
||||
interface TableValueProps {
|
||||
name: string;
|
||||
value: unknown;
|
||||
type: 'value' | 'boolean' | 'number' | 'select' | 'string';
|
||||
unit?: string | null | undefined;
|
||||
vtable?: string;
|
||||
}
|
||||
|
||||
const TableValue = ({
|
||||
name,
|
||||
value,
|
||||
type,
|
||||
unit,
|
||||
vtable
|
||||
}: TableValueProps) => {
|
||||
const { selectedCity } = useObjectsStore()
|
||||
|
||||
//Get available values
|
||||
const { data: tcbAll, isValidating } = useSWR(
|
||||
type === 'select' && selectedCity ? `/general/params/tcb?vtable=${vtable}&id_city=${selectedCity}` : null,
|
||||
(url) => fetcher(url, BASE_URL.ems).then(res => {
|
||||
if (Array.isArray(res)) {
|
||||
return res.map((el) => ({
|
||||
label: el.name || "",
|
||||
value: JSON.stringify(el.id)
|
||||
})) as ComboboxData
|
||||
}
|
||||
}),
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateIfStale: false
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Grid.Col span={4} style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Text size='xs' style={{ textWrap: 'wrap' }}>{name as string}</Text>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
{type === 'boolean' ?
|
||||
<Checkbox defaultChecked={value as boolean} />
|
||||
:
|
||||
type === 'number' ?
|
||||
<NumberInput
|
||||
size='xs'
|
||||
value={value as number}
|
||||
onChange={() => { }}
|
||||
suffix={unit ? ` ${unit}` : ''}
|
||||
/>
|
||||
:
|
||||
type === 'select' && !isValidating && tcbAll ?
|
||||
<Select size='xs' data={tcbAll} defaultValue={JSON.stringify(value)} />
|
||||
:
|
||||
type === 'string' ?
|
||||
<Textarea size='xs' defaultValue={value as string} autosize minRows={1} />
|
||||
:
|
||||
<Text size='xs'>{value as string}</Text>
|
||||
}
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default TableValue
|
@ -2,7 +2,7 @@ import { Coordinate, distance, rotate } from "ol/coordinate";
|
||||
import { containsExtent, Extent, getCenter, getHeight, getWidth } from "ol/extent";
|
||||
import Feature from "ol/Feature";
|
||||
import GeoJSON from "ol/format/GeoJSON";
|
||||
import { Circle, Geometry, LineString, Point, Polygon, SimpleGeometry } from "ol/geom";
|
||||
import { Circle, Geometry, LineString, Polygon, SimpleGeometry } from "ol/geom";
|
||||
import VectorLayer from "ol/layer/Vector";
|
||||
import VectorImageLayer from "ol/layer/VectorImage";
|
||||
import Map from "ol/Map";
|
||||
@ -20,13 +20,15 @@ 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, getMeasureShowSegments, getMeasureType, getTipPoint } from "../../store/map";
|
||||
import { getCurrentTool, getMeasureClearPrevious, getMeasureType, getTipPoint } from "../../store/map";
|
||||
import { VectorImage } from "ol/layer";
|
||||
import { MutableRefObject } from "react";
|
||||
|
||||
export function processLine(
|
||||
line: ILine,
|
||||
scaling: { w: number, h: number },
|
||||
mapCenter: Coordinate,
|
||||
linesLayer: React.MutableRefObject<VectorLayer<VectorSource<any>, any>>
|
||||
linesLayer: React.MutableRefObject<VectorImage<Feature<Geometry>, VectorSource<Feature<Geometry>>>>
|
||||
) {
|
||||
const x1 = line.x1 * scaling.w
|
||||
const y1 = line.y1 * scaling.h
|
||||
@ -53,7 +55,7 @@ export function processFigure(
|
||||
figure: IFigure,
|
||||
scaling: { w: number, h: number },
|
||||
mapCenter: Coordinate,
|
||||
figuresLayer: React.MutableRefObject<VectorLayer<VectorSource<any>, any>>
|
||||
figuresLayer: React.MutableRefObject<VectorImage<Feature<Geometry>, VectorSource<Feature<Geometry>>>>
|
||||
) {
|
||||
if (figure.figure_type_id == 1) {
|
||||
const width = figure.width * scaling.w
|
||||
@ -186,7 +188,6 @@ export const addInteractions = (
|
||||
measureModify: React.MutableRefObject<Modify>,
|
||||
) => {
|
||||
const currentTool = getCurrentTool()
|
||||
const showSegments = getMeasureShowSegments()
|
||||
const clearPrevious = getMeasureClearPrevious()
|
||||
const measureType = getMeasureType()
|
||||
const tipPoint = getTipPoint()
|
||||
@ -338,7 +339,7 @@ const zoomToFeature = (map: React.MutableRefObject<Map | null>, feature: Feature
|
||||
}
|
||||
|
||||
// Function to save features to localStorage
|
||||
export const saveFeatures = (layerRef: React.MutableRefObject<VectorLayer<VectorSource<any>, any> | null>) => {
|
||||
export const saveFeatures = (layerRef: MutableRefObject<VectorLayer<VectorSource> | null>) => {
|
||||
const features = layerRef.current?.getSource()?.getFeatures()
|
||||
if (features && features.length > 0) {
|
||||
const geoJSON = new GeoJSON()
|
||||
|
Reference in New Issue
Block a user