Object data

This commit is contained in:
cracklesparkle
2024-11-26 18:00:18 +09:00
parent a4513e7e7a
commit bd0a317e76
17 changed files with 1517 additions and 719 deletions

View File

@ -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(