forked from VinokurovVE/tests
Map experiments
This commit is contained in:
58
client/src/components/map/ChunkedUpload.tsx
Normal file
58
client/src/components/map/ChunkedUpload.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import React, { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
const ChunkedUpload = () => {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [uploadProgress, setUploadProgress] = useState<number>(0);
|
||||
|
||||
// Handle file selection
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files) {
|
||||
setFile(event.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
// Upload the file in chunks
|
||||
const uploadFile = async () => {
|
||||
if (!file) return;
|
||||
|
||||
const chunkSize = 1024 * 1024; // 1MB per chunk
|
||||
const totalChunks = Math.ceil(file.size / chunkSize);
|
||||
const fileId = `${file.name}-${Date.now()}`; // Unique file identifier
|
||||
let uploadedChunks = 0;
|
||||
|
||||
for (let start = 0; start < file.size; start += chunkSize) {
|
||||
const chunk = file.slice(start, start + chunkSize);
|
||||
const chunkNumber = Math.ceil(start / chunkSize) + 1;
|
||||
|
||||
try {
|
||||
await axios.post(`${import.meta.env.VITE_API_EMS_URL}/upload`, chunk, {
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'X-Chunk-Number': chunkNumber.toString(),
|
||||
'X-Total-Chunks': totalChunks.toString(),
|
||||
'X-File-Id': fileId,
|
||||
},
|
||||
});
|
||||
uploadedChunks++;
|
||||
setUploadProgress((uploadedChunks / totalChunks) * 100);
|
||||
} catch (error) {
|
||||
console.error('Chunk upload failed', error);
|
||||
// Implement retry logic if needed
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input type="file" onChange={handleFileChange} />
|
||||
<button onClick={uploadFile} disabled={!file}>
|
||||
Upload File
|
||||
</button>
|
||||
<div>Upload Progress: {uploadProgress.toFixed(2)}%</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChunkedUpload;
|
@ -4,29 +4,35 @@ import 'ol/ol.css'
|
||||
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 { ImageStatic, OSM, TileDebug, Vector as VectorSource, XYZ } from 'ol/source'
|
||||
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
|
||||
import { Divider, IconButton, Slider, Stack, Select as MUISelect, MenuItem, Box } from '@mui/material'
|
||||
import { Divider, IconButton, Slider, Stack, Select as MUISelect, MenuItem, Box, Typography } from '@mui/material'
|
||||
import { Add, Adjust, Api, CircleOutlined, OpenWith, RectangleOutlined, Straighten, Timeline, Undo, Warning } from '@mui/icons-material'
|
||||
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 { boundingExtent, containsExtent, Extent, getBottomLeft, getBottomRight, getCenter, getHeight, getTopLeft, getTopRight, getWidth } from 'ol/extent'
|
||||
import { containsExtent, Extent, getCenter, getHeight, getWidth } from 'ol/extent'
|
||||
import { drawingLayerStyle, regionsLayerStyle, selectStyle } from './MapStyles'
|
||||
import { googleMapsSatelliteSource, regionsLayerSource, yandexMapsSatelliteSource } from './MapSources'
|
||||
import { mapCenter } from './MapConstants'
|
||||
import { mapCenter, mapExtent } 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 Collection from 'ol/Collection'
|
||||
import { Coordinate, distance, rotate } from 'ol/coordinate'
|
||||
import { Coordinate } from 'ol/coordinate'
|
||||
import { Stroke, Fill, Circle as CircleStyle, Style } from 'ol/style'
|
||||
import { addCoordinateTransforms, addProjection, get, getTransform, Projection, transform } from 'ol/proj'
|
||||
import proj4 from 'proj4'
|
||||
import { calculateExtent, calculateRotationAngle, rotateProjection } from './mapUtils'
|
||||
import MapBrowserEvent from 'ol/MapBrowserEvent'
|
||||
import { get } from 'ol/proj'
|
||||
|
||||
const MapComponent = () => {
|
||||
const [currentCoordinate, setCurrentCoordinate] = useState<Coordinate | null>(null)
|
||||
const [currentZ, setCurrentZ] = useState<number | undefined>(undefined)
|
||||
const [currentX, setCurrentX] = useState<number | undefined>(undefined)
|
||||
const [currentY, setCurrentY] = useState<number | undefined>(undefined)
|
||||
|
||||
const mapElement = useRef<HTMLDivElement | null>(null)
|
||||
const [currentTool, setCurrentTool] = useState<Type | null>(null)
|
||||
|
||||
@ -191,88 +197,6 @@ const MapComponent = () => {
|
||||
};
|
||||
}
|
||||
|
||||
function rotateProjection(projection, angle, extent) {
|
||||
function rotateCoordinate(coordinate, angle, anchor) {
|
||||
var coord = rotate(
|
||||
[coordinate[0] - anchor[0], coordinate[1] - anchor[1]],
|
||||
angle
|
||||
);
|
||||
return [coord[0] + anchor[0], coord[1] + anchor[1]];
|
||||
}
|
||||
|
||||
function rotateTransform(coordinate: Coordinate) {
|
||||
return rotateCoordinate(coordinate, angle, getCenter(extent));
|
||||
}
|
||||
|
||||
function normalTransform(coordinate: Coordinate) {
|
||||
return rotateCoordinate(coordinate, -angle, getCenter(extent));
|
||||
}
|
||||
|
||||
var normalProjection = get(projection);
|
||||
|
||||
var rotatedProjection = new Projection({
|
||||
code:
|
||||
normalProjection.getCode() +
|
||||
":" +
|
||||
angle.toString() +
|
||||
":" +
|
||||
extent.toString(),
|
||||
units: normalProjection.getUnits(),
|
||||
extent: extent
|
||||
});
|
||||
addProjection(rotatedProjection);
|
||||
|
||||
addCoordinateTransforms(
|
||||
"EPSG:4326",
|
||||
rotatedProjection,
|
||||
function (coordinate) {
|
||||
return rotateTransform(transform(coordinate, "EPSG:4326", projection));
|
||||
},
|
||||
function (coordinate) {
|
||||
return transform(normalTransform(coordinate), projection, "EPSG:4326");
|
||||
}
|
||||
);
|
||||
|
||||
addCoordinateTransforms(
|
||||
"EPSG:3857",
|
||||
rotatedProjection,
|
||||
function (coordinate) {
|
||||
return rotateTransform(transform(coordinate, "EPSG:3857", projection));
|
||||
},
|
||||
function (coordinate) {
|
||||
return transform(normalTransform(coordinate), projection, "EPSG:3857");
|
||||
}
|
||||
);
|
||||
|
||||
// also set up transforms with any projections defined using proj4
|
||||
if (typeof proj4 !== "undefined") {
|
||||
var projCodes = Object.keys(proj4.defs);
|
||||
projCodes.forEach(function (code) {
|
||||
var proj4Projection = get(code);
|
||||
if (!getTransform(proj4Projection, rotatedProjection)) {
|
||||
addCoordinateTransforms(
|
||||
proj4Projection,
|
||||
rotatedProjection,
|
||||
function (coordinate) {
|
||||
return rotateTransform(
|
||||
transform(coordinate, proj4Projection, projection)
|
||||
);
|
||||
},
|
||||
function (coordinate) {
|
||||
return transform(
|
||||
normalTransform(coordinate),
|
||||
projection,
|
||||
proj4Projection
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return rotatedProjection;
|
||||
}
|
||||
|
||||
const handleImageDrop = useCallback((event: any) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@ -383,41 +307,6 @@ const MapComponent = () => {
|
||||
}
|
||||
});
|
||||
|
||||
const calculateCentroid = (bottomLeft: Coordinate, topLeft: Coordinate, topRight: Coordinate, bottomRight: Coordinate) => {
|
||||
const x = (bottomLeft[0] + topLeft[0] + topRight[0] + bottomRight[0]) / 4;
|
||||
const y = (bottomLeft[1] + topLeft[1] + topRight[1] + bottomRight[1]) / 4;
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
const calculateRotationAngle = (bottomLeft: Coordinate, bottomRight: Coordinate) => {
|
||||
// Calculate the difference in x and y coordinates between bottom right and bottom left
|
||||
const deltaX = bottomRight[0] - bottomLeft[0];
|
||||
const deltaY = bottomRight[1] - bottomLeft[1];
|
||||
|
||||
// Calculate the angle using atan2
|
||||
const angle = Math.atan2(deltaY, deltaX);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
const calculateExtent = (bottomLeft: Coordinate, topLeft: Coordinate, topRight: Coordinate, bottomRight: Coordinate) => {
|
||||
const width = distance(bottomLeft, bottomRight);
|
||||
const height = distance(bottomLeft, topLeft);
|
||||
|
||||
// Calculate the centroid of the polygon
|
||||
const [centerX, centerY] = calculateCentroid(bottomLeft, topLeft, topRight, bottomRight);
|
||||
|
||||
// Define the extent based on the center and dimensions
|
||||
const extent = [
|
||||
centerX - width / 2, // minX
|
||||
centerY - height / 2, // minY
|
||||
centerX + width / 2, // maxX
|
||||
centerY + height / 2 // maxY
|
||||
];
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
// Function to update the image layer with a new source when extent changes
|
||||
const updateImageSource = () => {
|
||||
const newExtent = polygonFeature.getGeometry()?.getExtent();
|
||||
@ -430,10 +319,34 @@ const MapComponent = () => {
|
||||
if (newExtent && bottomLeft && bottomRight && topRight && topLeft) {
|
||||
const originalExtent = calculateExtent(bottomLeft, topLeft, topRight, bottomRight)
|
||||
|
||||
const worldExtent = get('EPSG:3857')?.getExtent() as Extent
|
||||
const zoomLevel = Number(map.current?.getView().getZoom()?.toFixed(0))
|
||||
const { tileX: blX, tileY: blY } = getGridCellPosition(bottomLeft[0], bottomLeft[1], worldExtent, zoomLevel)
|
||||
const { tileX: tlX, tileY: tlY } = getGridCellPosition(topLeft[0], topLeft[1], worldExtent, zoomLevel)
|
||||
const { tileX: trX, tileY: trY } = getGridCellPosition(topRight[0], topRight[1], worldExtent, zoomLevel)
|
||||
const { tileX: brX, tileY: brY } = getGridCellPosition(bottomRight[0], topRight[1], worldExtent, zoomLevel)
|
||||
const minX = Math.min(blX, tlX, trX, brX)
|
||||
const maxX = Math.max(blX, tlX, trX, brX)
|
||||
const minY = Math.min(blY, tlY, trY, brY)
|
||||
const maxY = Math.max(blY, tlY, trY, brY)
|
||||
|
||||
const mapWidth = Math.abs(worldExtent[0] - worldExtent[2])
|
||||
const mapHeight = Math.abs(worldExtent[1] - worldExtent[3])
|
||||
const tileWidth = mapWidth / (zoomLevel * 4)
|
||||
const tileHeight = mapHeight / (zoomLevel * 4)
|
||||
const newMinX = worldExtent[0] + (tileWidth * minX)
|
||||
const newMaxX = worldExtent[0] + (tileWidth * (maxX + 1))
|
||||
|
||||
const newMinY = worldExtent[1] + (tileHeight * (maxY + 1))
|
||||
const newMaxY = worldExtent[1] + (tileHeight * minY)
|
||||
|
||||
console.log('Tile slippy bounds: ', minX, maxX, minY, maxY)
|
||||
console.log('Tile bounds: ', newMinX, newMaxX, newMinY, newMaxY)
|
||||
|
||||
const newImageSource = new ImageStatic({
|
||||
url: imageUrl,
|
||||
imageExtent: originalExtent,
|
||||
projection: rotateProjection('EPSG:3857', -calculateRotationAngle(bottomLeft, bottomRight), originalExtent)
|
||||
projection: rotateProjection('EPSG:3857', calculateRotationAngle(bottomLeft, bottomRight), originalExtent)
|
||||
});
|
||||
imageLayer.current.setSource(newImageSource);
|
||||
}
|
||||
@ -535,6 +448,36 @@ 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,
|
||||
@ -589,7 +532,9 @@ const MapComponent = () => {
|
||||
})
|
||||
|
||||
map.current = new Map({
|
||||
layers: [baseLayer.current, satLayer.current, regionsLayer.current, drawingLayer.current, imageLayer.current, overlayLayer.current],
|
||||
layers: [baseLayer.current, new TileLayer({
|
||||
source: new TileDebug(),
|
||||
}), satLayer.current, regionsLayer.current, drawingLayer.current, imageLayer.current, overlayLayer.current],
|
||||
target: mapElement.current as HTMLDivElement,
|
||||
view: new View({
|
||||
center: mapCenter,
|
||||
@ -599,6 +544,16 @@ const MapComponent = () => {
|
||||
}),
|
||||
})
|
||||
|
||||
map.current.on('pointermove', function (e: MapBrowserEvent<any>) {
|
||||
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)))
|
||||
setCurrentZ(Number(map.current?.getView().getZoom()?.toFixed(0)))
|
||||
setCurrentX(tileX)
|
||||
setCurrentY(tileY)
|
||||
})
|
||||
|
||||
const modify = new Modify({ source: drawingLayerSource.current })
|
||||
map.current.addInteraction(modify)
|
||||
|
||||
@ -678,6 +633,16 @@ const MapComponent = () => {
|
||||
<IconButton title='Добавить подложку'>
|
||||
<Add />
|
||||
</IconButton>
|
||||
|
||||
<Typography>
|
||||
{currentCoordinate?.[0]}-{currentCoordinate?.[1]}
|
||||
</Typography>
|
||||
|
||||
<Typography>
|
||||
Z={currentZ}
|
||||
X={currentX}
|
||||
Y={currentY}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack my={1} spacing={1} direction='row' divider={<Divider orientation='vertical' flexItem />}>
|
||||
|
||||
|
127
client/src/components/map/mapUtils.ts
Normal file
127
client/src/components/map/mapUtils.ts
Normal file
@ -0,0 +1,127 @@
|
||||
import { Coordinate, distance, rotate } from "ol/coordinate";
|
||||
import { Extent, getCenter } from "ol/extent";
|
||||
import { addCoordinateTransforms, addProjection, get, getTransform, Projection, ProjectionLike, transform } from "ol/proj";
|
||||
import proj4 from "proj4";
|
||||
|
||||
function rotateProjection(projection: ProjectionLike, angle: number, extent: Extent) {
|
||||
function rotateCoordinate(coordinate: Coordinate, angle: number, anchor: Coordinate) {
|
||||
var coord = rotate(
|
||||
[coordinate[0] - anchor[0], coordinate[1] - anchor[1]],
|
||||
angle
|
||||
);
|
||||
return [coord[0] + anchor[0], coord[1] + anchor[1]];
|
||||
}
|
||||
|
||||
function rotateTransform(coordinate: Coordinate) {
|
||||
return rotateCoordinate(coordinate, angle, getCenter(extent));
|
||||
}
|
||||
|
||||
function normalTransform(coordinate: Coordinate) {
|
||||
return rotateCoordinate(coordinate, -angle, getCenter(extent));
|
||||
}
|
||||
|
||||
var normalProjection = get(projection);
|
||||
|
||||
if (normalProjection) {
|
||||
var rotatedProjection = new Projection({
|
||||
code: normalProjection.getCode() + ":" + angle.toString() + ":" + extent.toString(),
|
||||
units: normalProjection.getUnits(),
|
||||
extent: extent
|
||||
});
|
||||
addProjection(rotatedProjection);
|
||||
|
||||
addCoordinateTransforms(
|
||||
"EPSG:4326",
|
||||
rotatedProjection,
|
||||
function (coordinate) {
|
||||
return rotateTransform(transform(coordinate, "EPSG:4326", projection));
|
||||
},
|
||||
function (coordinate) {
|
||||
return transform(normalTransform(coordinate), projection, "EPSG:4326");
|
||||
}
|
||||
);
|
||||
|
||||
addCoordinateTransforms(
|
||||
"EPSG:3857",
|
||||
rotatedProjection,
|
||||
function (coordinate) {
|
||||
return rotateTransform(transform(coordinate, "EPSG:3857", projection));
|
||||
},
|
||||
function (coordinate) {
|
||||
return transform(normalTransform(coordinate), projection, "EPSG:3857");
|
||||
}
|
||||
);
|
||||
|
||||
// also set up transforms with any projections defined using proj4
|
||||
if (typeof proj4 !== "undefined") {
|
||||
var projCodes = Object.keys(proj4.defs);
|
||||
projCodes.forEach(function (code) {
|
||||
var proj4Projection = get(code) as Projection;
|
||||
if (proj4Projection) {
|
||||
if (!getTransform(proj4Projection, rotatedProjection)) {
|
||||
addCoordinateTransforms(
|
||||
proj4Projection,
|
||||
rotatedProjection,
|
||||
function (coordinate) {
|
||||
return rotateTransform(
|
||||
transform(coordinate, proj4Projection, projection)
|
||||
);
|
||||
},
|
||||
function (coordinate) {
|
||||
return transform(
|
||||
normalTransform(coordinate),
|
||||
projection,
|
||||
proj4Projection
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return rotatedProjection;
|
||||
}
|
||||
}
|
||||
|
||||
const calculateCentroid = (bottomLeft: Coordinate, topLeft: Coordinate, topRight: Coordinate, bottomRight: Coordinate) => {
|
||||
const x = (bottomLeft[0] + topLeft[0] + topRight[0] + bottomRight[0]) / 4;
|
||||
const y = (bottomLeft[1] + topLeft[1] + topRight[1] + bottomRight[1]) / 4;
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
function calculateRotationAngle(bottomLeft: Coordinate, bottomRight: Coordinate) {
|
||||
// Calculate the difference in x and y coordinates between bottom right and bottom left
|
||||
const deltaX = bottomRight[0] - bottomLeft[0];
|
||||
const deltaY = bottomRight[1] - bottomLeft[1];
|
||||
|
||||
// Calculate the angle using atan2
|
||||
const angle = -Math.atan2(deltaY, deltaX);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
function calculateExtent(bottomLeft: Coordinate, topLeft: Coordinate, topRight: Coordinate, bottomRight: Coordinate) {
|
||||
const width = distance(bottomLeft, bottomRight);
|
||||
const height = distance(bottomLeft, topLeft);
|
||||
|
||||
// Calculate the centroid of the polygon
|
||||
const [centerX, centerY] = calculateCentroid(bottomLeft, topLeft, topRight, bottomRight);
|
||||
|
||||
// Define the extent based on the center and dimensions
|
||||
const extent = [
|
||||
centerX - width / 2, // minX
|
||||
centerY - height / 2, // minY
|
||||
centerX + width / 2, // maxX
|
||||
centerY + height / 2 // maxY
|
||||
];
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
export {
|
||||
rotateProjection,
|
||||
calculateRotationAngle,
|
||||
calculateExtent,
|
||||
calculateCentroid
|
||||
}
|
Reference in New Issue
Block a user