Map testing, custom table based on Tanstack Table

This commit is contained in:
cracklesparkle
2024-10-25 10:02:40 +09:00
parent edb6ae00fb
commit 115c6ec417
13 changed files with 1956 additions and 125 deletions

View File

@ -11,26 +11,29 @@ import { Type } from 'ol/geom/Geometry'
import { click, never, noModifierKeys, platformModifierKeyOnly, primaryAction, shiftKeyOnly } from 'ol/events/condition'
import Feature from 'ol/Feature'
import { SatelliteMapsProvider } from '../../interfaces/map'
import { containsExtent, Extent, getCenter, getHeight, getWidth } from 'ol/extent'
import { containsExtent, Extent } from 'ol/extent'
import { drawingLayerStyle, regionsLayerStyle, selectStyle } 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, MultiPoint, Point, Polygon, SimpleGeometry } from 'ol/geom'
import { fromExtent } from 'ol/geom/Polygon'
import { Circle, LineString, MultiPoint, Point, Polygon, SimpleGeometry } from 'ol/geom'
import { fromCircle, fromExtent } from 'ol/geom/Polygon'
import Collection from 'ol/Collection'
import { Coordinate } from 'ol/coordinate'
import { Stroke, Fill, Circle as CircleStyle, Style } from 'ol/style'
import { calculateExtent, calculateRotationAngle, rotateProjection } from './mapUtils'
import { Stroke, Fill, Circle as CircleStyle, Style, Text } from 'ol/style'
import { calculateCenter, calculateExtent, calculateRotationAngle, rotateProjection } from './mapUtils'
import MapBrowserEvent from 'ol/MapBrowserEvent'
import { fromLonLat, get } from 'ol/proj'
import { fromLonLat, get, transform } from 'ol/proj'
import { useCities } from '../../hooks/swrHooks'
import useSWR from 'swr'
import { fetcher } from '../../http/axiosInstance'
import { BASE_URL } from '../../constants'
import { Accordion, ActionIcon, Box, Flex, Select as MantineSelect, MantineStyleProp, rem, Slider, useMantineColorScheme } from '@mantine/core'
import { Accordion, ActionIcon, Box, Button, Flex, Select as MantineSelect, MantineStyleProp, rem, Slider, useMantineColorScheme } from '@mantine/core'
import { IconApi, IconArrowBackUp, IconArrowsMove, IconCircle, IconExclamationCircle, IconLine, IconPlus, IconPoint, IconPolygon, IconRuler, IconTable, IconUpload } from '@tabler/icons-react'
import { getGridCellPosition } from './mapUtils'
import { IFigure, ILine } from '../../interfaces/gis'
import { Height } from '@mui/icons-material'
const MapComponent = () => {
const { cities } = useCities(100, 1)
@ -97,6 +100,10 @@ const MapComponent = () => {
source: new VectorSource()
}))
const figuresLayer = useRef<VectorLayer>(new VectorLayer({
source: new VectorSource()
}))
const regionsLayer = useRef<VectorImageLayer>(new VectorImageLayer({
source: regionsLayerSource,
style: regionsLayerStyle
@ -208,49 +215,7 @@ const MapComponent = () => {
}),
});
function calculateCenter(geometry: SimpleGeometry) {
let center, coordinates, minRadius;
const type = geometry.getType();
if (type === 'Polygon') {
let x = 0;
let y = 0;
let i = 0;
coordinates = (geometry as Polygon).getCoordinates()[0].slice(1);
coordinates.forEach(function (coordinate) {
x += coordinate[0];
y += coordinate[1];
i++;
});
center = [x / i, y / i];
} else if (type === 'LineString') {
center = (geometry as LineString).getCoordinateAt(0.5);
coordinates = geometry.getCoordinates();
} else {
center = getCenter(geometry.getExtent());
}
let sqDistances;
if (coordinates) {
sqDistances = coordinates.map(function (coordinate: Coordinate) {
const dx = coordinate[0] - center[0];
const dy = coordinate[1] - center[1];
return dx * dx + dy * dy;
});
minRadius = Math.sqrt(Math.max.apply(Math, sqDistances)) / 3;
} else {
minRadius =
Math.max(
getWidth(geometry.getExtent()),
getHeight(geometry.getExtent()),
) / 3;
}
return {
center: center,
coordinates: coordinates,
minRadius: minRadius,
sqDistances: sqDistances,
};
}
// tile processing
const handleImageDrop = useCallback((event: any) => {
event.preventDefault();
event.stopPropagation();
@ -398,10 +363,10 @@ const MapComponent = () => {
const tileWidth = mapWidth / (Math.sqrt(Math.pow(4, zoomLevel)))
const tileHeight = mapHeight / (Math.sqrt(Math.pow(4, zoomLevel)))
let minPosX = minX - (tilesH / 2)
let maxPosX = maxX - (tilesH / 2) + 1
let minPosY = -(minY - (tilesH / 2))
let maxPosY = -(maxY - (tilesH / 2) + 1)
const minPosX = minX - (tilesH / 2)
const maxPosX = maxX - (tilesH / 2) + 1
const minPosY = -(minY - (tilesH / 2))
const maxPosY = -(maxY - (tilesH / 2) + 1)
console.log(`tileWidth: ${tileWidth} minPosX: ${minPosX} maxPosX: ${maxPosX} minPosY: ${minPosY} maxPosY: ${maxPosY}`)
const newMinX = tileWidth * minPosX
@ -551,36 +516,6 @@ const MapComponent = () => {
})
}
function getTilesPerSide(zoom: number) {
return Math.pow(2, zoom)
}
function normalize(value: number, min: number, max: number) {
return (value - min) / (max - min)
}
function getTileIndex(normalized: number, tilesPerSide: number) {
return Math.floor(normalized * tilesPerSide)
}
function getGridCellPosition(x: number, y: number, extent: Extent, zoom: number) {
const tilesPerSide = getTilesPerSide(zoom);
const minX = extent[0]
const minY = extent[1]
const maxX = extent[2]
const maxY = extent[3]
// Normalize the coordinates
const xNormalized = normalize(x, minX, maxX);
const yNormalized = normalize(y, minY, maxY);
// Get tile indices
const tileX = getTileIndex(xNormalized, tilesPerSide);
const tileY = getTileIndex(1 - yNormalized, tilesPerSide);
return { tileX, tileY };
}
useEffect(() => {
drawingLayer.current = new VectorLayer({
source: drawingLayerSource.current,
@ -641,11 +576,11 @@ const MapComponent = () => {
map.current = new Map({
controls: [],
layers: [baseLayer.current, satLayer.current, regionsLayer.current, citiesLayer.current, drawingLayer.current, imageLayer.current, overlayLayer.current, nodeLayer.current],
layers: [baseLayer.current, satLayer.current, regionsLayer.current, citiesLayer.current, figuresLayer.current, drawingLayer.current, imageLayer.current, overlayLayer.current, nodeLayer.current],
target: mapElement.current as HTMLDivElement,
view: new View({
center: mapCenter,//center: fromLonLat([130.401113, 67.797368]),
zoom: 16,
center: transform([129.7659541, 62.009504], 'EPSG:4326', 'EPSG:3857'),//center: fromLonLat([130.401113, 67.797368]),
zoom: 17,
maxZoom: 21,
//extent: mapExtent,
}),
@ -729,7 +664,7 @@ const MapComponent = () => {
}
};
const [satelliteOpacity, setSatelliteOpacity] = useState<number>(0)
const [satelliteOpacity, setSatelliteOpacity] = useState<number>(1)
const [statusText, setStatusText] = useState('')
@ -797,7 +732,7 @@ const MapComponent = () => {
if (Array.isArray(nodes)) {
nodes.map(node => {
if (node.shape_type === 'LINE') {
let coordinates: Coordinate[] = []
const coordinates: Coordinate[] = []
if (Array.isArray(node.shape)) {
node.shape.map((point: any) => {
const coordinate = [point.x as number, point.y as number] as Coordinate
@ -811,6 +746,102 @@ const MapComponent = () => {
}
}, [nodes])
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')
})
})
];
}
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')}`
})
})
];
}
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')
})
})
];
}
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')
})
})
];
}
return (
<Box w={'100%'} h={'calc(100% - 64px)'} mah={'100%'} flex={'1'} pos={'relative'}>
<ActionIcon.Group orientation='vertical' pos='absolute' top='8px' right='8px' style={{ zIndex: 1, backdropFilter: 'blur(8px)', backgroundColor: colorScheme === 'light' ? '#FFFFFFAA' : '#000000AA', borderRadius: '4px' }}>
@ -918,7 +949,160 @@ const MapComponent = () => {
<Accordion variant='filled' style={{ backgroundColor: 'transparent' }} defaultValue='Объекты'>
<Accordion.Item key={'s'} value={'Объекты'}>
<Accordion.Control icon={<IconTable />}>{'Объекты'}</Accordion.Control>
<Accordion.Panel>{'ASd'}</Accordion.Panel>
<Accordion.Panel>
<Button
onClick={async () => {
//open()
const city_id = 145
const year = 2023
const figuresLimit = 10000
const linesLimit = 10000
figuresLayer.current.getSource()?.clear()
try {
// const response = await fetch(`${import.meta.env.VITE_API_EMS_URL}/gis/images/all?city_id=${city_id}`, {
// method: 'GET',
// headers: {
// 'Content-Type': 'application/json',
// },
// })
//const responseData = await response.json()
const scaling = {
w: 10000, // responseData[0].width
h: 10000 // responseData[0].width
}
const figuresResponse = await fetch(`${import.meta.env.VITE_API_EMS_URL}/gis/figures/all?city_id=${city_id}&year=${year}&offset=0&limit=${figuresLimit}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
const linesResponse = await fetch(`${import.meta.env.VITE_API_EMS_URL}/gis/lines/all?city_id=${city_id}&year=${year}&offset=0&limit=${linesLimit}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
await linesResponse.json().then(linesData => {
linesData.map((line: ILine) => {
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('object_id', line.object_id)
figuresLayer.current?.getSource()?.addFeature(feature)
})
})
await figuresResponse.json().then(figuresData => {
figuresData.map((figure: IFigure) => {
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('object_id', figure.object_id)
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.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('angle', figure.angle)
feature1.setStyle(fourthStyleFunction(feature1))
figuresLayer.current?.getSource()?.addFeature(feature1)
}
})
})
} catch (err) {
console.error('No data')
}
}}
>
Test
</Button>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
</Flex>
@ -956,7 +1140,7 @@ const MapComponent = () => {
}}
>
</div>
</Box>
</Box >
);
};