forked from VinokurovVE/tests
Object data
This commit is contained in:
13
client/package-lock.json
generated
13
client/package-lock.json
generated
@ -55,6 +55,7 @@
|
||||
"react-router-dom": "^6.23.1",
|
||||
"recharts": "^2.12.7",
|
||||
"swr": "^2.2.5",
|
||||
"uuid": "^11.0.3",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -12121,6 +12122,18 @@
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz",
|
||||
"integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"bin": {
|
||||
"uuid": "dist/esm/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/varint": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
|
||||
|
@ -58,6 +58,7 @@
|
||||
"react-router-dom": "^6.23.1",
|
||||
"recharts": "^2.12.7",
|
||||
"swr": "^2.2.5",
|
||||
"uuid": "^11.0.3",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,190 @@
|
||||
import Feature, { FeatureLike } from "ol/Feature";
|
||||
import { Text } from "ol/style";
|
||||
import Fill from "ol/style/Fill";
|
||||
import { FlatStyleLike } from "ol/style/flat";
|
||||
import Stroke from "ol/style/Stroke";
|
||||
import Style from "ol/style/Style";
|
||||
import { calculateCenter } from "./mapUtils";
|
||||
import CircleStyle from "ol/style/Circle";
|
||||
import { MultiPoint, Point } from "ol/geom";
|
||||
|
||||
export const highlightStyleYellow = new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'yellow',
|
||||
width: 3,
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 0, 0.3)',
|
||||
}),
|
||||
});
|
||||
|
||||
export const highlightStyleRed = new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'red',
|
||||
width: 3,
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 0, 0.3)',
|
||||
}),
|
||||
});
|
||||
|
||||
export function overlayStyle(feature: FeatureLike) {
|
||||
const styles = [new Style({
|
||||
geometry: function (feature) {
|
||||
const modifyGeometry = feature.get('modifyGeometry');
|
||||
return modifyGeometry ? modifyGeometry.geometry : feature.getGeometry();
|
||||
},
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 255, 0.2)',
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: '#ffcc33',
|
||||
width: 2,
|
||||
}),
|
||||
image: new CircleStyle({
|
||||
radius: 7,
|
||||
fill: new Fill({
|
||||
color: '#ffcc33',
|
||||
}),
|
||||
}),
|
||||
})]
|
||||
const modifyGeometry = feature.get('modifyGeometry')
|
||||
const geometry = modifyGeometry ? modifyGeometry.geometry : feature.getGeometry()
|
||||
const result = calculateCenter(geometry)
|
||||
const center = result.center
|
||||
if (center) {
|
||||
styles.push(
|
||||
new Style({
|
||||
geometry: new Point(center),
|
||||
image: new CircleStyle({
|
||||
radius: 4,
|
||||
fill: new Fill({
|
||||
color: '#ff3333'
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
const coordinates = result.coordinates
|
||||
if (coordinates) {
|
||||
const minRadius = result.minRadius
|
||||
const sqDistances = result.sqDistances
|
||||
const rsq = minRadius * minRadius
|
||||
if (Array.isArray(sqDistances)) {
|
||||
const points = coordinates.filter(function (_coordinate, index) {
|
||||
return sqDistances[index] > rsq
|
||||
})
|
||||
styles.push(
|
||||
new Style({
|
||||
geometry: new MultiPoint(points),
|
||||
image: new CircleStyle({
|
||||
radius: 4,
|
||||
fill: new Fill({
|
||||
color: '#33cc33'
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return styles
|
||||
}
|
||||
|
||||
export function styleFunction(feature: Feature) {
|
||||
return [
|
||||
new Style({
|
||||
fill: new Fill({
|
||||
color: 'rgba(255,255,255,0.4)'
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: '#3399CC',
|
||||
width: 1.25
|
||||
}),
|
||||
text: new Text({
|
||||
font: '12px Calibri,sans-serif',
|
||||
fill: new Fill({ color: '#000' }),
|
||||
stroke: new Stroke({
|
||||
color: '#fff', width: 2
|
||||
}),
|
||||
// get the text from the feature - `this` is ol.Feature
|
||||
// and show only under certain resolution
|
||||
text: feature.get('object_id')
|
||||
})
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
export function firstStyleFunction(feature: Feature) {
|
||||
return [
|
||||
new Style({
|
||||
fill: new Fill({
|
||||
color: 'rgba(255,255,255,0.4)'
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: 'red',
|
||||
width: 1.25
|
||||
}),
|
||||
text: new Text({
|
||||
font: '12px Calibri,sans-serif',
|
||||
fill: new Fill({ color: '#000' }),
|
||||
stroke: new Stroke({
|
||||
color: '#fff', width: 2
|
||||
}),
|
||||
// get the text from the feature - `this` is ol.Feature
|
||||
// and show only under certain resolution
|
||||
text: feature.get('object_id')
|
||||
})
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
export function thirdStyleFunction(feature: Feature) {
|
||||
return [
|
||||
new Style({
|
||||
fill: new Fill({
|
||||
color: 'rgba(255,255,255,0.4)'
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: '#33ccb3',
|
||||
width: 1.25
|
||||
}),
|
||||
text: new Text({
|
||||
font: '12px Calibri,sans-serif',
|
||||
fill: new Fill({ color: '#000' }),
|
||||
stroke: new Stroke({
|
||||
color: '#fff', width: 2
|
||||
}),
|
||||
// get the text from the feature - `this` is ol.Feature
|
||||
// and show only under certain resolution
|
||||
text: feature.get('object_id')
|
||||
})
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
export function fourthStyleFunction(feature: Feature) {
|
||||
return [
|
||||
new Style({
|
||||
fill: new Fill({
|
||||
color: 'rgba(255,255,255,0.4)'
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: '#3399CC',
|
||||
width: 1.25
|
||||
}),
|
||||
text: new Text({
|
||||
font: '12px Calibri,sans-serif',
|
||||
fill: new Fill({ color: '#000' }),
|
||||
stroke: new Stroke({
|
||||
color: '#fff', width: 2
|
||||
}),
|
||||
// get the text from the feature - `this` is ol.Feature
|
||||
// and show only under certain resolution
|
||||
text: `${feature.get('object_id')}\n ${feature.get('angle')}`
|
||||
})
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
const drawingLayerStyle: FlatStyleLike = {
|
||||
'fill-color': 'rgba(255, 255, 255, 0.2)',
|
||||
|
@ -1,25 +1,22 @@
|
||||
import { ActionIcon, MantineColorScheme } from '@mantine/core'
|
||||
import { IconApi, IconArrowBackUp, IconArrowsMove, IconCircle, IconExclamationCircle, IconLine, IconPoint, IconPolygon, IconRuler } from '@tabler/icons-react'
|
||||
import { Type } from 'ol/geom/Geometry'
|
||||
import React from 'react'
|
||||
import { setCurrentTool, useMapStore } from '../../../store/map';
|
||||
|
||||
interface IToolbarProps {
|
||||
currentTool: Type | null;
|
||||
onSave: () => void;
|
||||
onRemove: () => void;
|
||||
handleToolSelect: (tool: Type) => void;
|
||||
onMover: () => void;
|
||||
colorScheme: MantineColorScheme;
|
||||
}
|
||||
|
||||
const MapToolbar = ({
|
||||
currentTool,
|
||||
onSave,
|
||||
onRemove,
|
||||
handleToolSelect,
|
||||
onMover,
|
||||
colorScheme
|
||||
}: IToolbarProps) => {
|
||||
const mapState = useMapStore()
|
||||
|
||||
return (
|
||||
<ActionIcon.Group orientation='vertical' pos='absolute' top='8px' right='8px' style={{ zIndex: 1, backdropFilter: 'blur(8px)', backgroundColor: colorScheme === 'light' ? '#FFFFFFAA' : '#000000AA', borderRadius: '4px' }}>
|
||||
<ActionIcon size='lg' variant='transparent' onClick={() => {
|
||||
@ -38,36 +35,36 @@ const MapToolbar = ({
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant={currentTool === 'Point' ? 'filled' : 'transparent'}
|
||||
variant={mapState.currentTool === 'Point' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
handleToolSelect('Point')
|
||||
setCurrentTool('Point')
|
||||
}}>
|
||||
<IconPoint />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant={currentTool === 'LineString' ? 'filled' : 'transparent'}
|
||||
variant={mapState.currentTool === 'LineString' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
handleToolSelect('LineString')
|
||||
setCurrentTool('LineString')
|
||||
}}>
|
||||
<IconLine />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant={currentTool === 'Polygon' ? 'filled' : 'transparent'}
|
||||
variant={mapState.currentTool === 'Polygon' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
handleToolSelect('Polygon')
|
||||
setCurrentTool('Polygon')
|
||||
}}>
|
||||
<IconPolygon />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant={currentTool === 'Circle' ? 'filled' : 'transparent'}
|
||||
variant={mapState.currentTool === 'Circle' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
handleToolSelect('Circle')
|
||||
setCurrentTool('Circle')
|
||||
}}>
|
||||
<IconCircle />
|
||||
</ActionIcon>
|
||||
@ -82,8 +79,10 @@ const MapToolbar = ({
|
||||
|
||||
<ActionIcon
|
||||
size='lg'
|
||||
variant='transparent'
|
||||
>
|
||||
variant={mapState.currentTool === 'Measure' ? 'filled' : 'transparent'}
|
||||
onClick={() => {
|
||||
setCurrentTool('Measure')
|
||||
}}>
|
||||
<IconRuler />
|
||||
</ActionIcon>
|
||||
</ActionIcon.Group>
|
||||
|
39
client/src/components/map/MapTree/MapTreeCheckbox.tsx
Normal file
39
client/src/components/map/MapTree/MapTreeCheckbox.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { Checkbox, Group, RenderTreeNodePayload } from "@mantine/core";
|
||||
import { IconChevronDown } from "@tabler/icons-react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const MapTreeCheckbox = ({
|
||||
node,
|
||||
expanded,
|
||||
hasChildren,
|
||||
elementProps,
|
||||
tree,
|
||||
}: RenderTreeNodePayload) => {
|
||||
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
|
||||
checked={checked}
|
||||
indeterminate={indeterminate}
|
||||
onClick={() => (!checked ? tree.checkNode(node.value) : tree.uncheckNode(node.value))}
|
||||
/>
|
||||
|
||||
<Group gap={5} onClick={() => tree.toggleExpanded(node.value)}>
|
||||
<span>{node.label}</span>
|
||||
|
||||
{hasChildren && (
|
||||
<IconChevronDown
|
||||
size={14}
|
||||
style={{ transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' }}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
);
|
||||
};
|
199
client/src/components/map/Measure/MeasureStyles.ts
Normal file
199
client/src/components/map/Measure/MeasureStyles.ts
Normal file
@ -0,0 +1,199 @@
|
||||
import { FeatureLike } from "ol/Feature";
|
||||
import { LineString, Point, Polygon } from "ol/geom";
|
||||
import Geometry, { Type } from "ol/geom/Geometry";
|
||||
import { Fill, RegularShape, Stroke, Style, Text } from "ol/style";
|
||||
import CircleStyle from "ol/style/Circle";
|
||||
import { getArea, getLength } from 'ol/sphere'
|
||||
import { Modify } from "ol/interaction";
|
||||
import { getMeasureShowSegments } from "../../../store/map";
|
||||
|
||||
export const style = new Style({
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 255, 0.2)',
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: 'rgba(0, 0, 0, 0.5)',
|
||||
lineDash: [10, 10],
|
||||
width: 2,
|
||||
}),
|
||||
image: new CircleStyle({
|
||||
radius: 5,
|
||||
stroke: new Stroke({
|
||||
color: 'rgba(0, 0, 0, 0.7)',
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 255, 0.2)',
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const labelStyle = new Style({
|
||||
text: new Text({
|
||||
font: '14px Calibri,sans-serif',
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
}),
|
||||
backgroundFill: new Fill({
|
||||
color: 'rgba(0, 0, 0, 0.7)',
|
||||
}),
|
||||
padding: [3, 3, 3, 3],
|
||||
textBaseline: 'bottom',
|
||||
offsetY: -15,
|
||||
}),
|
||||
image: new RegularShape({
|
||||
radius: 8,
|
||||
points: 3,
|
||||
angle: Math.PI,
|
||||
displacement: [0, 10],
|
||||
fill: new Fill({
|
||||
color: 'rgba(0, 0, 0, 0.7)',
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const tipStyle = new Style({
|
||||
text: new Text({
|
||||
font: '12px Calibri,sans-serif',
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
}),
|
||||
backgroundFill: new Fill({
|
||||
color: 'rgba(0, 0, 0, 0.4)',
|
||||
}),
|
||||
padding: [2, 2, 2, 2],
|
||||
textAlign: 'left',
|
||||
offsetX: 15,
|
||||
}),
|
||||
});
|
||||
|
||||
export const modifyStyle = new Style({
|
||||
image: new CircleStyle({
|
||||
radius: 5,
|
||||
stroke: new Stroke({
|
||||
color: 'rgba(0, 0, 0, 0.7)',
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(0, 0, 0, 0.4)',
|
||||
}),
|
||||
}),
|
||||
text: new Text({
|
||||
text: 'Drag to modify',
|
||||
font: '12px Calibri,sans-serif',
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
}),
|
||||
backgroundFill: new Fill({
|
||||
color: 'rgba(0, 0, 0, 0.7)',
|
||||
}),
|
||||
padding: [2, 2, 2, 2],
|
||||
textAlign: 'left',
|
||||
offsetX: 15,
|
||||
}),
|
||||
});
|
||||
|
||||
export const segmentStyle = new Style({
|
||||
text: new Text({
|
||||
font: '12px Calibri,sans-serif',
|
||||
fill: new Fill({
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
}),
|
||||
backgroundFill: new Fill({
|
||||
color: 'rgba(0, 0, 0, 0.4)',
|
||||
}),
|
||||
padding: [2, 2, 2, 2],
|
||||
textBaseline: 'bottom',
|
||||
offsetY: -12,
|
||||
}),
|
||||
image: new RegularShape({
|
||||
radius: 6,
|
||||
points: 3,
|
||||
angle: Math.PI,
|
||||
displacement: [0, 8],
|
||||
fill: new Fill({
|
||||
color: 'rgba(0, 0, 0, 0.4)',
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
const formatLength = function (line: Geometry) {
|
||||
const length = getLength(line);
|
||||
let output;
|
||||
if (length > 100) {
|
||||
output = Math.round((length / 1000) * 100) / 100 + ' km';
|
||||
} else {
|
||||
output = Math.round(length * 100) / 100 + ' m';
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
const formatArea = function (polygon: Geometry) {
|
||||
const area = getArea(polygon);
|
||||
let output;
|
||||
if (area > 10000) {
|
||||
output = Math.round((area / 1000000) * 100) / 100 + ' km\xB2';
|
||||
} else {
|
||||
output = Math.round(area * 100) / 100 + ' m\xB2';
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
export function measureStyleFunction(
|
||||
feature: FeatureLike,
|
||||
drawType?: Type,
|
||||
tip?: string,
|
||||
setTipPoint?: React.Dispatch<React.SetStateAction<Point | null>>,
|
||||
modify?: React.MutableRefObject<Modify>
|
||||
) {
|
||||
const styles = [];
|
||||
const geometry = feature.getGeometry();
|
||||
const type = geometry?.getType();
|
||||
const segmentStyles = [segmentStyle];
|
||||
|
||||
const segments = getMeasureShowSegments()
|
||||
|
||||
if (!geometry) return
|
||||
|
||||
let point, label, line;
|
||||
if (!drawType || drawType === type || type === 'Point') {
|
||||
styles.push(style);
|
||||
if (type === 'Polygon') {
|
||||
point = (geometry as Polygon).getInteriorPoint();
|
||||
label = formatArea(geometry as Polygon);
|
||||
line = new LineString((geometry as Polygon).getCoordinates()[0]);
|
||||
} else if (type === 'LineString') {
|
||||
point = new Point((geometry as Polygon).getLastCoordinate());
|
||||
label = formatLength(geometry as LineString);
|
||||
line = geometry;
|
||||
}
|
||||
}
|
||||
if (segments && line) {
|
||||
let count = 0;
|
||||
(line as LineString).forEachSegment(function (a, b) {
|
||||
const segment = new LineString([a, b]);
|
||||
const label = formatLength(segment);
|
||||
if (segmentStyles.length - 1 < count) {
|
||||
segmentStyles.push(segmentStyle.clone());
|
||||
}
|
||||
const segmentPoint = new Point(segment.getCoordinateAt(0.5));
|
||||
segmentStyles[count].setGeometry(segmentPoint);
|
||||
segmentStyles[count].getText()?.setText(label);
|
||||
styles.push(segmentStyles[count]);
|
||||
count++;
|
||||
});
|
||||
}
|
||||
if (label) {
|
||||
labelStyle.setGeometry(point as Geometry);
|
||||
labelStyle.getText()?.setText(label);
|
||||
styles.push(labelStyle);
|
||||
}
|
||||
if (
|
||||
tip &&
|
||||
type === 'Point' &&
|
||||
!modify?.current.getOverlay()?.getSource()?.getFeatures().length
|
||||
) {
|
||||
setTipPoint?.(geometry as Point);
|
||||
tipStyle.getText()?.setText(tip);
|
||||
styles.push(tipStyle);
|
||||
}
|
||||
return styles;
|
||||
}
|
@ -1,49 +1,83 @@
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from '../../http/axiosInstance'
|
||||
import { BASE_URL } from '../../constants'
|
||||
import { Checkbox, Grid } from '@mantine/core'
|
||||
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'
|
||||
|
||||
interface ObjectParameterProps {
|
||||
showLabel?: boolean,
|
||||
param: IObjectParam,
|
||||
}
|
||||
|
||||
const ObjectParameter = ({
|
||||
id_param,
|
||||
value
|
||||
}: IObjectParam) => {
|
||||
param,
|
||||
showLabel = true
|
||||
}: ObjectParameterProps) => {
|
||||
const { data: paramData } = useSWR(
|
||||
`/general/params/all?param_id=${id_param}`,
|
||||
`/general/params/all?param_id=${param.id_param}`,
|
||||
(url) => fetcher(url, BASE_URL.ems).then(res => res[0] as IParam),
|
||||
{
|
||||
revalidateOnFocus: false
|
||||
}
|
||||
)
|
||||
|
||||
const Parameter = (type: string, name: string, value: unknown) => {
|
||||
const Parameter = (type: string, name: string, value: unknown, vtable: string) => {
|
||||
switch (type) {
|
||||
case 'bit':
|
||||
return (
|
||||
<Grid align='center' gutter='xl'>
|
||||
<Grid.Col span={1}>
|
||||
<Flex direction='row' align='center' gap='sm'>
|
||||
<Checkbox defaultChecked={value as boolean} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={'auto'}>
|
||||
<p>{name}</p>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<Text>{name}</Text>
|
||||
</Flex>
|
||||
)
|
||||
case 'varchar(200)':
|
||||
return (
|
||||
<Text>
|
||||
{decodeDoubleEncodedString(value as string)}
|
||||
</Text>
|
||||
)
|
||||
case 'varchar(5)':
|
||||
return (
|
||||
<Text>
|
||||
{decodeDoubleEncodedString(value as string)}
|
||||
</Text>
|
||||
)
|
||||
case 'bigint':
|
||||
return (
|
||||
<Text>
|
||||
{(value as string)}
|
||||
</Text>
|
||||
)
|
||||
case 'GTCB':
|
||||
return (
|
||||
<TCBParameter value={value as string} vtable={vtable} />
|
||||
)
|
||||
case 'TCB':
|
||||
return (
|
||||
<TCBParameter value={value as string} vtable={vtable} />
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<div>
|
||||
Неподдерживаемый параметр
|
||||
{type}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{paramData &&
|
||||
Parameter(paramData.format, paramData.name, value)
|
||||
<Stack gap={0}>
|
||||
{showLabel &&
|
||||
<Divider my="xs" label={paramData.name} labelPosition="left" />
|
||||
}
|
||||
</div>
|
||||
{Parameter(paramData.format, paramData.name, param.value, paramData.vtable)}
|
||||
</Stack>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
100
client/src/components/map/TCBParameter.tsx
Normal file
100
client/src/components/map/TCBParameter.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
import React from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from '../../http/axiosInstance'
|
||||
import { BASE_URL } from '../../constants'
|
||||
import { Text } from '@mantine/core'
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
const TCBParameter = ({
|
||||
value,
|
||||
vtable
|
||||
}: ITCBParameterProps) => {
|
||||
|
||||
//Get value
|
||||
const { data: tcbValue } = useSWR(
|
||||
`/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
|
||||
}
|
||||
)
|
||||
|
||||
const TCBValue = (vtable: string) => {
|
||||
switch (vtable) {
|
||||
case 'vStreets':
|
||||
return (
|
||||
<Text>
|
||||
{JSON.stringify(tcbValue)}
|
||||
</Text>
|
||||
)
|
||||
case 'tTypes':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue as tType)?.name}
|
||||
</Text>
|
||||
)
|
||||
case 'vPipesGround':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue)?.name}
|
||||
</Text>
|
||||
)
|
||||
case 'vRepairEvent':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue)?.name}
|
||||
</Text>
|
||||
)
|
||||
case 'vPipesMaterial':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue)?.name}
|
||||
</Text>
|
||||
)
|
||||
case 'vBoilers':
|
||||
return (
|
||||
<Text>
|
||||
{(tcbValue)?.name}
|
||||
</Text>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<Text>
|
||||
{JSON.stringify(tcbValue)}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
TCBValue(vtable)
|
||||
)
|
||||
}
|
||||
|
||||
export default TCBParameter
|
@ -1,12 +1,368 @@
|
||||
import { Coordinate, distance, rotate } from "ol/coordinate";
|
||||
import { Extent, getCenter, getHeight, getWidth } from "ol/extent";
|
||||
import { LineString, Polygon, SimpleGeometry } from "ol/geom";
|
||||
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 VectorLayer from "ol/layer/Vector";
|
||||
import VectorImageLayer from "ol/layer/VectorImage";
|
||||
import Map from "ol/Map";
|
||||
import { addCoordinateTransforms, addProjection, get, getTransform, Projection, ProjectionLike, transform } from "ol/proj";
|
||||
import VectorSource from "ol/source/Vector";
|
||||
import proj4 from "proj4";
|
||||
import { firstStyleFunction, fourthStyleFunction, selectStyle, styleFunction, thirdStyleFunction } from "./MapStyles";
|
||||
import { Type } from "ol/geom/Geometry";
|
||||
import { Draw, Modify, Snap } from "ol/interaction";
|
||||
import { noModifierKeys } from "ol/events/condition";
|
||||
import { IGeometryType, IRectCoords } from "../../interfaces/map";
|
||||
import { uploadCoordinates } from "../../actions/map";
|
||||
import { ImageStatic } from "ol/source";
|
||||
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";
|
||||
|
||||
export function processLine(
|
||||
line: ILine,
|
||||
scaling: { w: number, h: number },
|
||||
mapCenter: Coordinate,
|
||||
linesLayer: React.MutableRefObject<VectorLayer<VectorSource<any>, any>>
|
||||
) {
|
||||
const x1 = line.x1 * scaling.w
|
||||
const y1 = line.y1 * scaling.h
|
||||
const x2 = line.x2 * scaling.w
|
||||
const y2 = line.y2 * scaling.h
|
||||
|
||||
const center = [mapCenter[0], mapCenter[1]]
|
||||
|
||||
const testCoords = [
|
||||
[center[0] + x1, center[1] - y1],
|
||||
[center[0] + x2, center[1] - y2],
|
||||
]
|
||||
|
||||
const feature = new Feature(new LineString(testCoords))
|
||||
feature.setStyle(styleFunction(feature))
|
||||
feature.set('type', line.type)
|
||||
feature.set('planning', line.planning)
|
||||
feature.set('object_id', line.object_id)
|
||||
|
||||
linesLayer.current?.getSource()?.addFeature(feature)
|
||||
}
|
||||
|
||||
export function processFigure(
|
||||
figure: IFigure,
|
||||
scaling: { w: number, h: number },
|
||||
mapCenter: Coordinate,
|
||||
figuresLayer: React.MutableRefObject<VectorLayer<VectorSource<any>, any>>
|
||||
) {
|
||||
if (figure.figure_type_id == 1) {
|
||||
const width = figure.width * scaling.w
|
||||
const height = figure.height * scaling.h
|
||||
|
||||
const left = figure.left * scaling.w
|
||||
const top = figure.top * scaling.h
|
||||
|
||||
const centerX = mapCenter[0] + left + (width / 2)
|
||||
const centerY = mapCenter[1] - top - (height / 2)
|
||||
|
||||
const radius = width / 2;
|
||||
const circleGeom = new Circle([centerX, centerY], radius)
|
||||
|
||||
const ellipseGeom = fromCircle(circleGeom, 64)
|
||||
ellipseGeom.scale(1, height / width)
|
||||
|
||||
const feature = new Feature(ellipseGeom)
|
||||
|
||||
feature.setStyle(firstStyleFunction(feature))
|
||||
feature.set('type', figure.type)
|
||||
feature.set('object_id', figure.object_id)
|
||||
feature.set('planning', figure.planning)
|
||||
figuresLayer.current?.getSource()?.addFeature(feature)
|
||||
}
|
||||
|
||||
if (figure.figure_type_id == 3) {
|
||||
const x = figure.left * scaling.w
|
||||
const y = figure.top * scaling.h
|
||||
|
||||
const center = [mapCenter[0] + x, mapCenter[1] - y]
|
||||
|
||||
const coords = figure.points?.split(' ').map(pair => {
|
||||
const [x, y] = pair.split(';').map(Number)
|
||||
return [
|
||||
center[0] + (x * scaling.w),
|
||||
center[1] - (y * scaling.h)
|
||||
]
|
||||
})
|
||||
|
||||
if (coords) {
|
||||
const polygon = new Polygon([coords])
|
||||
|
||||
const feature = new Feature({
|
||||
geometry: polygon
|
||||
})
|
||||
|
||||
feature.set('object_id', figure.object_id)
|
||||
feature.set('planning', figure.planning)
|
||||
feature.set('type', figure.type)
|
||||
feature.setStyle(thirdStyleFunction(feature))
|
||||
figuresLayer.current?.getSource()?.addFeature(feature)
|
||||
}
|
||||
}
|
||||
|
||||
if (figure.figure_type_id == 4) {
|
||||
const width = figure.width * scaling.w
|
||||
const height = figure.height * scaling.h
|
||||
const left = figure.left * scaling.w
|
||||
const top = figure.top * scaling.h
|
||||
|
||||
const halfWidth = width / 2
|
||||
const halfHeight = height / 2
|
||||
|
||||
const center = [mapCenter[0] + left + halfWidth, mapCenter[1] - top - halfHeight]
|
||||
|
||||
const testCoords = [
|
||||
[center[0] - halfWidth, center[1] - halfHeight],
|
||||
[center[0] - halfWidth, center[1] + halfHeight],
|
||||
[center[0] + halfWidth, center[1] + halfHeight],
|
||||
[center[0] + halfWidth, center[1] - halfHeight],
|
||||
[center[0] - halfWidth, center[1] - halfHeight]
|
||||
]
|
||||
|
||||
const geometry1 = new Polygon([testCoords])
|
||||
const anchor1 = center
|
||||
geometry1.rotate(-figure.angle * Math.PI / 180, anchor1)
|
||||
const feature1 = new Feature(geometry1)
|
||||
feature1.set('object_id', figure.object_id)
|
||||
feature1.set('planning', figure.planning)
|
||||
feature1.set('type', figure.type)
|
||||
feature1.set('angle', figure.angle)
|
||||
feature1.setStyle(fourthStyleFunction(feature1))
|
||||
figuresLayer.current?.getSource()?.addFeature(feature1)
|
||||
}
|
||||
}
|
||||
|
||||
// Function to update the image layer with a new source when extent changes
|
||||
export const updateImageSource = (
|
||||
imageUrl: string,
|
||||
imageLayer: React.MutableRefObject<ImageLayer<ImageStatic>>,
|
||||
polygonFeature: Feature<Polygon>,
|
||||
setPolygonExtent: (value: React.SetStateAction<Extent | undefined>) => void,
|
||||
setRectCoords: React.Dispatch<React.SetStateAction<IRectCoords | undefined>>
|
||||
) => {
|
||||
const newExtent = polygonFeature.getGeometry()?.getExtent();
|
||||
|
||||
const bottomLeft = polygonFeature.getGeometry()?.getCoordinates()[0][0]
|
||||
const topLeft = polygonFeature.getGeometry()?.getCoordinates()[0][1]
|
||||
const topRight = polygonFeature.getGeometry()?.getCoordinates()[0][2]
|
||||
const bottomRight = polygonFeature.getGeometry()?.getCoordinates()[0][3]
|
||||
|
||||
setRectCoords({
|
||||
bl: bottomLeft,
|
||||
tl: topLeft,
|
||||
tr: topRight,
|
||||
br: bottomRight
|
||||
})
|
||||
|
||||
setPolygonExtent(newExtent)
|
||||
|
||||
if (newExtent && bottomLeft && bottomRight && topRight && topLeft) {
|
||||
const originalExtent = calculateExtent(bottomLeft, topLeft, topRight, bottomRight)
|
||||
const newImageSource = new ImageStatic({
|
||||
url: imageUrl,
|
||||
imageExtent: originalExtent,
|
||||
projection: rotateProjection('EPSG:3857', calculateRotationAngle(bottomLeft, bottomRight), originalExtent)
|
||||
});
|
||||
imageLayer.current.setSource(newImageSource);
|
||||
}
|
||||
};
|
||||
|
||||
export const addInteractions = (
|
||||
drawingLayerSource: React.MutableRefObject<VectorSource<Feature<Geometry>>>,
|
||||
draw: React.MutableRefObject<Draw | null>,
|
||||
map: React.MutableRefObject<Map | null>,
|
||||
snap: React.MutableRefObject<Snap | null>,
|
||||
measureDraw: React.MutableRefObject<Draw | null>,
|
||||
measureSource: React.MutableRefObject<VectorSource<Feature<Geometry>>>,
|
||||
measureModify: React.MutableRefObject<Modify>,
|
||||
) => {
|
||||
const currentTool = getCurrentTool()
|
||||
const showSegments = getMeasureShowSegments()
|
||||
const clearPrevious = getMeasureClearPrevious()
|
||||
const measureType = getMeasureType()
|
||||
const tipPoint = getTipPoint()
|
||||
|
||||
if (currentTool !== 'Measure') {
|
||||
draw.current = new Draw({
|
||||
source: drawingLayerSource.current,
|
||||
type: currentTool as Type,
|
||||
condition: noModifierKeys
|
||||
})
|
||||
|
||||
draw.current.on('drawend', function (s) {
|
||||
console.log(s.feature.getGeometry()?.getType())
|
||||
let type: IGeometryType = 'POLYGON'
|
||||
|
||||
switch (s.feature.getGeometry()?.getType()) {
|
||||
case 'LineString':
|
||||
type = 'LINE'
|
||||
break
|
||||
case 'Polygon':
|
||||
type = 'POLYGON'
|
||||
break
|
||||
default:
|
||||
type = 'POLYGON'
|
||||
break
|
||||
}
|
||||
const coordinates = (s.feature.getGeometry() as SimpleGeometry).getCoordinates() as Coordinate[]
|
||||
uploadCoordinates(coordinates, type)
|
||||
})
|
||||
|
||||
map?.current?.addInteraction(draw.current)
|
||||
snap.current = new Snap({ source: drawingLayerSource.current })
|
||||
map?.current?.addInteraction(snap.current)
|
||||
}
|
||||
|
||||
if (currentTool == 'Measure') {
|
||||
const drawType = measureType;
|
||||
const activeTip =
|
||||
'Кликните, чтобы продолжить рисовать ' +
|
||||
(drawType === 'Polygon' ? 'многоугольник' : 'линию');
|
||||
const idleTip = 'Кликните, чтобы начать измерение';
|
||||
let tip = idleTip;
|
||||
|
||||
measureDraw.current = new Draw({
|
||||
source: measureSource.current,
|
||||
type: drawType,
|
||||
style: function (feature) {
|
||||
return measureStyleFunction(feature, drawType, tip);
|
||||
},
|
||||
});
|
||||
measureDraw.current.on('drawstart', function () {
|
||||
if (clearPrevious) {
|
||||
measureSource.current.clear();
|
||||
}
|
||||
measureModify.current.setActive(false);
|
||||
tip = activeTip;
|
||||
});
|
||||
measureDraw.current.on('drawend', function () {
|
||||
modifyStyle.setGeometry(tipPoint as Geometry);
|
||||
measureModify.current.setActive(true);
|
||||
map.current?.once('pointermove', function () {
|
||||
modifyStyle.setGeometry('');
|
||||
});
|
||||
tip = idleTip;
|
||||
});
|
||||
measureModify.current.setActive(true);
|
||||
map.current?.addInteraction(measureDraw.current);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
selectedRegion.current = null
|
||||
}
|
||||
|
||||
if (map.current) {
|
||||
map.current.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
|
||||
if (layer === regionsLayer.current) {
|
||||
selectedRegion.current = feature as Feature
|
||||
// Zoom to the selected feature
|
||||
zoomToFeature(map, selectedRegion.current)
|
||||
|
||||
return true
|
||||
} else return false
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
// Show current selected region
|
||||
map.current?.on('pointermove', function (e) {
|
||||
if (selectedRegion.current !== null) {
|
||||
selectedRegion.current.setStyle(undefined)
|
||||
selectedRegion.current = null
|
||||
}
|
||||
|
||||
if (map.current) {
|
||||
map.current.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
|
||||
if (layer === regionsLayer.current) {
|
||||
selectedRegion.current = feature as Feature
|
||||
selectedRegion.current.setStyle(selectStyle)
|
||||
|
||||
if (feature.get('district')) {
|
||||
setStatusText(feature.get('district'))
|
||||
}
|
||||
|
||||
return true
|
||||
} else return false
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Hide regions layer when fully visible
|
||||
map.current?.on('moveend', function () {
|
||||
const viewExtent = map.current?.getView().calculateExtent(map.current.getSize())
|
||||
const features = regionsLayer.current.getSource()?.getFeatures()
|
||||
|
||||
let isViewCovered = false
|
||||
|
||||
features?.forEach((feature: Feature) => {
|
||||
const featureExtent = feature?.getGeometry()?.getExtent()
|
||||
if (viewExtent && featureExtent) {
|
||||
if (containsExtent(featureExtent, viewExtent)) {
|
||||
isViewCovered = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
regionsLayer.current.setVisible(!isViewCovered)
|
||||
})
|
||||
}
|
||||
|
||||
const zoomToFeature = (map: React.MutableRefObject<Map | null>, feature: Feature) => {
|
||||
const geometry = feature.getGeometry()
|
||||
const extent = geometry?.getExtent()
|
||||
|
||||
if (map.current && extent) {
|
||||
map.current.getView().fit(extent, {
|
||||
duration: 300,
|
||||
maxZoom: 19,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Function to save features to localStorage
|
||||
export const saveFeatures = (layerRef: React.MutableRefObject<VectorLayer<VectorSource<any>, any> | null>) => {
|
||||
const features = layerRef.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
|
||||
export const loadFeatures = (layerSource: React.MutableRefObject<VectorSource<Feature<Geometry>>>) => {
|
||||
const savedFeatures = localStorage.getItem('savedFeatures')
|
||||
if (savedFeatures) {
|
||||
const geoJSON = new GeoJSON()
|
||||
const features = geoJSON.readFeatures(savedFeatures, {
|
||||
featureProjection: 'EPSG:4326', // Ensure the projection is correct
|
||||
})
|
||||
layerSource.current?.addFeatures(features) // Add features to the vector source
|
||||
//drawingLayer.current?.getSource()?.changed()
|
||||
}
|
||||
}
|
||||
|
||||
function rotateProjection(projection: ProjectionLike, angle: number, extent: Extent) {
|
||||
function rotateCoordinate(coordinate: Coordinate, angle: number, anchor: Coordinate) {
|
||||
var coord = rotate(
|
||||
const coord = rotate(
|
||||
[coordinate[0] - anchor[0], coordinate[1] - anchor[1]],
|
||||
angle
|
||||
);
|
||||
@ -21,10 +377,10 @@ function rotateProjection(projection: ProjectionLike, angle: number, extent: Ext
|
||||
return rotateCoordinate(coordinate, -angle, getCenter(extent));
|
||||
}
|
||||
|
||||
var normalProjection = get(projection);
|
||||
const normalProjection = get(projection);
|
||||
|
||||
if (normalProjection) {
|
||||
var rotatedProjection = new Projection({
|
||||
const rotatedProjection = new Projection({
|
||||
code: normalProjection.getCode() + ":" + angle.toString() + ":" + extent.toString(),
|
||||
units: normalProjection.getUnits(),
|
||||
extent: extent
|
||||
@ -55,9 +411,9 @@ function rotateProjection(projection: ProjectionLike, angle: number, extent: Ext
|
||||
|
||||
// also set up transforms with any projections defined using proj4
|
||||
if (typeof proj4 !== "undefined") {
|
||||
var projCodes = Object.keys(proj4.defs);
|
||||
const projCodes = Object.keys(proj4.defs);
|
||||
projCodes.forEach(function (code) {
|
||||
var proj4Projection = get(code) as Projection;
|
||||
const proj4Projection = get(code) as Projection;
|
||||
if (proj4Projection) {
|
||||
if (!getTransform(proj4Projection, rotatedProjection)) {
|
||||
addCoordinateTransforms(
|
||||
@ -177,7 +533,7 @@ function calculateCenter(geometry: SimpleGeometry) {
|
||||
const dy = coordinate[1] - center[1];
|
||||
return dx * dx + dy * dy;
|
||||
});
|
||||
minRadius = Math.sqrt(Math.max.apply(Math, sqDistances)) / 3;
|
||||
minRadius = Math.sqrt(Math.max(...sqDistances)) / 3;
|
||||
} else {
|
||||
minRadius =
|
||||
Math.max(
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Coordinate } from "ol/coordinate";
|
||||
|
||||
export interface SatelliteMapsProviders {
|
||||
google: 'google';
|
||||
yandex: 'yandex';
|
||||
@ -11,3 +13,10 @@ export interface IGeometryTypes {
|
||||
}
|
||||
|
||||
export type IGeometryType = IGeometryTypes[keyof IGeometryTypes]
|
||||
|
||||
export interface IRectCoords {
|
||||
bl: Coordinate | undefined,
|
||||
tl: Coordinate | undefined,
|
||||
tr: Coordinate | undefined,
|
||||
br: Coordinate | undefined
|
||||
}
|
85
client/src/store/map.ts
Normal file
85
client/src/store/map.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { create } from 'zustand';
|
||||
import { ToolType } from '../types/tools';
|
||||
import { Point } from 'ol/geom';
|
||||
import Map from 'ol/Map';
|
||||
|
||||
interface MapState {
|
||||
currentTool: ToolType,
|
||||
measureType: "LineString" | "Polygon",
|
||||
measureShowSegments: boolean,
|
||||
measureClearPrevious: boolean,
|
||||
tipPoint: Point | null,
|
||||
map: Map | null
|
||||
}
|
||||
|
||||
export const useMapStore = create<MapState>(() => ({
|
||||
currentTool: null,
|
||||
measureType: "LineString",
|
||||
measureShowSegments: true,
|
||||
measureClearPrevious: true,
|
||||
tipPoint: null,
|
||||
map: null
|
||||
}));
|
||||
|
||||
const getMap = () => {
|
||||
return useMapStore.getState().map
|
||||
}
|
||||
|
||||
const setMap = (map: Map | null) => {
|
||||
useMapStore.setState(() => ({ map: map }))
|
||||
}
|
||||
|
||||
const setTipPoint = (tipPoint: Point | null) => {
|
||||
useMapStore.setState(() => ({ tipPoint: tipPoint }))
|
||||
}
|
||||
|
||||
const getTipPoint = () => {
|
||||
return useMapStore.getState().tipPoint
|
||||
}
|
||||
|
||||
const setMeasureType = (tool: "LineString" | "Polygon") => {
|
||||
useMapStore.setState(() => ({ measureType: tool }))
|
||||
}
|
||||
|
||||
const getMeasureType = () => {
|
||||
return useMapStore.getState().measureType
|
||||
}
|
||||
|
||||
const setCurrentTool = (tool: ToolType) => {
|
||||
tool === useMapStore.getState().currentTool
|
||||
? useMapStore.setState(() => ({ currentTool: null }))
|
||||
: useMapStore.setState(() => ({ currentTool: tool }))
|
||||
}
|
||||
|
||||
const getCurrentTool = () => {
|
||||
return useMapStore.getState().currentTool
|
||||
}
|
||||
|
||||
const getMeasureShowSegments = () => {
|
||||
return useMapStore.getState().measureShowSegments
|
||||
}
|
||||
|
||||
const getMeasureClearPrevious = () => {
|
||||
return useMapStore.getState().measureClearPrevious
|
||||
}
|
||||
|
||||
const setMeasureShowSegments = (bool: boolean) => {
|
||||
useMapStore.setState(() => ({ measureShowSegments: bool }))
|
||||
}
|
||||
|
||||
const setMeasureClearPrevious = (bool: boolean) => {
|
||||
useMapStore.setState(() => ({ measureClearPrevious: bool }))
|
||||
}
|
||||
|
||||
export {
|
||||
setCurrentTool,
|
||||
getCurrentTool,
|
||||
setMeasureShowSegments,
|
||||
setMeasureClearPrevious,
|
||||
getMeasureShowSegments,
|
||||
getMeasureClearPrevious,
|
||||
setMeasureType,
|
||||
getMeasureType,
|
||||
getTipPoint,
|
||||
setTipPoint
|
||||
}
|
12
client/src/types/tools.ts
Normal file
12
client/src/types/tools.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export type ToolType =
|
||||
"Point" |
|
||||
"LineString" |
|
||||
"LinearRing" |
|
||||
"Polygon" |
|
||||
"MultiPoint" |
|
||||
"MultiLineString" |
|
||||
"MultiPolygon" |
|
||||
"GeometryCollection" |
|
||||
"Circle" |
|
||||
"Measure" |
|
||||
null
|
44
client/src/utils/format.ts
Normal file
44
client/src/utils/format.ts
Normal file
@ -0,0 +1,44 @@
|
||||
// CP437 Character Map
|
||||
const CP437_MAP = [
|
||||
'\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '◙', '♂', '♀', '♪', '♫', '☼', '►',
|
||||
'◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼', ' ', '!', '"',
|
||||
'#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4',
|
||||
'5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|',
|
||||
'}', '~', '⌂', 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä',
|
||||
'Å', 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ', 'á',
|
||||
'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»', '░', '▒', '▓',
|
||||
'│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐', '└', '┴', '┬', '├', '─',
|
||||
'┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧', '╨', '╤', '╥', '╙', '╘', '╒', '╓',
|
||||
'╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀', 'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ',
|
||||
'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩', '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·',
|
||||
'√', 'ⁿ', '²', '■', ' '
|
||||
];
|
||||
|
||||
function decodeCP437ToBytes(garbledString: string) {
|
||||
const bytes = [];
|
||||
for (const char of garbledString) {
|
||||
const byte = CP437_MAP.indexOf(char);
|
||||
if (byte === -1) {
|
||||
//console.warn(`Character '${char}' not found in CP437 map`);
|
||||
bytes.push(63); // '?' as a placeholder
|
||||
}
|
||||
bytes.push(byte);
|
||||
}
|
||||
return Uint8Array.from(bytes);
|
||||
}
|
||||
|
||||
function decodeWindows1251FromBytes(byteArray: any) {
|
||||
const decoder = new TextDecoder('windows-1251');
|
||||
return decoder.decode(byteArray);
|
||||
}
|
||||
|
||||
export function decodeDoubleEncodedString(garbledString: string) {
|
||||
// Step 1: Decode from CP437 to bytes
|
||||
const bytes = decodeCP437ToBytes(garbledString);
|
||||
|
||||
// Step 2: Decode bytes as WINDOWS-1251
|
||||
return decodeWindows1251FromBytes(bytes);
|
||||
}
|
@ -6592,6 +6592,11 @@ utrie@^1.0.2:
|
||||
dependencies:
|
||||
base64-arraybuffer "^1.0.2"
|
||||
|
||||
uuid@^11.0.3:
|
||||
version "11.0.3"
|
||||
resolved "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz"
|
||||
integrity sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==
|
||||
|
||||
varint@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz"
|
||||
|
@ -57,8 +57,32 @@ router.get('/objects/all', async (req: Request, res: Response) => {
|
||||
|
||||
router.get('/objects/list', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { city_id, year, planning } = req.query
|
||||
const { city_id, year, planning, type } = req.query
|
||||
|
||||
if (type) {
|
||||
const result = await tediousQuery(
|
||||
`
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
vObjects
|
||||
WHERE
|
||||
vObjects.id_city = ${city_id}
|
||||
AND vObjects.year = ${year}
|
||||
AND type = ${type}
|
||||
AND
|
||||
(
|
||||
CASE
|
||||
WHEN TRY_CAST(vObjects.planning AS BIT) IS NOT NULL THEN TRY_CAST(vObjects.planning AS BIT)
|
||||
WHEN vObjects.planning = 'TRUE' THEN 1
|
||||
WHEN vObjects.planning = 'FALSE' THEN 0
|
||||
ELSE NULL
|
||||
END
|
||||
) = ${planning};
|
||||
`
|
||||
)
|
||||
res.status(200).json(result)
|
||||
} else {
|
||||
const result = await tediousQuery(
|
||||
`
|
||||
SELECT
|
||||
@ -86,6 +110,7 @@ router.get('/objects/list', async (req: Request, res: Response) => {
|
||||
`
|
||||
)
|
||||
res.status(200).json(result)
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(500)
|
||||
}
|
||||
@ -151,4 +176,37 @@ router.get('/params/all', async (req: Request, res: Response) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Get value from TCB parameter
|
||||
router.get('/params/tcb', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { vtable, id, offset, limit } = req.query
|
||||
|
||||
if (!vtable) {
|
||||
res.status(500)
|
||||
}
|
||||
|
||||
if (id) {
|
||||
const result = await tediousQuery(
|
||||
`
|
||||
SELECT * FROM nGeneral..${vtable}
|
||||
WHERE id = '${id}'
|
||||
`
|
||||
)
|
||||
res.status(200).json(result)
|
||||
} else {
|
||||
const result = await tediousQuery(
|
||||
`
|
||||
SELECT * FROM nGeneral..${vtable}
|
||||
ORDER BY object_id
|
||||
OFFSET ${Number(offset) || 0} ROWS
|
||||
FETCH NEXT ${Number(limit) || 10} ROWS ONLY;
|
||||
`
|
||||
)
|
||||
res.status(200).json(result)
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(500)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
Reference in New Issue
Block a user