moving features: write to sqlite modified features

This commit is contained in:
2025-12-22 17:35:52 +09:00
parent e21f75f5e0
commit fa516b3a20
8 changed files with 452 additions and 27 deletions

View File

@ -22,6 +22,7 @@ import VectorImageLayer from "ol/layer/VectorImage";
import VectorSource from "ol/source/Vector";
import Map from "ol/Map";
import { Icon, Style } from "ol/style";
import axiosInstance from "../../http/axiosInstanceNest";
export function getCitySettings() {
return {
@ -94,11 +95,26 @@ export const addFigures = (
if (figuresData.length > 0) {
const geoJsonObject = {
type: "FeatureCollection",
features: figuresData.map((figure: IFigure) => processFigure(
figure,
settings.scale,
[center[0], center[1]]
)),
features: figuresData.map((figure: IFigure) => {
if (figure.modified) {
console.log("found modified", JSON.parse(figure.modified))
return {
...JSON.parse(figure.modified), properties: {
year: figure.year,
figure_type_id: figure.figure_type_id,
type: figure.type,
object_id: figure.object_id,
planning: figure.planning
}
}
} else {
return processFigure(
figure,
settings.scale,
[center[0], center[1]]
)
}
}),
}
const features = new GeoJSON().readFeatures(geoJsonObject)
@ -127,7 +143,22 @@ export const addLines = (
if (linesData.length > 0) {
const geoJsonObject = {
type: "FeatureCollection",
features: linesData.map((line: ILine) => processLine(line, settings.scale, [center[0], center[1]])),
features: linesData.map((line: ILine) => {
if (line.modified) {
return {
...JSON.parse(line.modified), properties: {
year: line.year,
type: line.type,
geometry_type: 'line',
object_id: line.object_id,
planning: line.planning,
}
}
} else {
return processLine(line, settings.scale, [center[0], center[1]])
}
}),
}
const features = new GeoJSON().readFeatures(geoJsonObject)
@ -144,6 +175,39 @@ export const calculateAngle = (coords: [number, number][]) => {
return Math.atan2(dy, dx); // Angle in radians
}
export function processLineFeatureToLine(
feature: Feature,
scaling: number,
mapCenter: Coordinate
): Partial<ILine> {
const geometry = feature.getGeometry() as LineString;
// Get the line coordinates
const coordinates = geometry.getCoordinates();
if (coordinates.length < 2) {
throw new Error('Line must have at least 2 points');
}
// Get start and end points
const startPoint = coordinates[0];
const endPoint = coordinates[coordinates.length - 1];
// Convert from map coordinates to database coordinates
const x1 = (startPoint[0] - mapCenter[0]) / scaling;
const y1 = (mapCenter[1] - startPoint[1]) / scaling;
const x2 = (endPoint[0] - mapCenter[0]) / scaling;
const y2 = (mapCenter[1] - endPoint[1]) / scaling;
return {
x1: parseFloat(x1.toFixed(6)),
y1: parseFloat(y1.toFixed(6)),
x2: parseFloat(x2.toFixed(6)),
y2: parseFloat(y2.toFixed(6)),
};
}
export function processLine(
line: ILine,
scaling: number,
@ -167,6 +231,7 @@ export function processLine(
type: "Feature",
geometry: new GeoJSON().writeGeometryObject(geometry),
properties: {
year: line.year,
type: line.type,
geometry_type: 'line',
object_id: line.object_id,
@ -176,6 +241,158 @@ export function processLine(
}
}
export function processFeatureToFigure(
feature: Feature,
figureTypeId: number,
scaling: number,
mapCenter: Coordinate,
): Partial<IFigure> {
const geometry = feature.getGeometry()
const properties = feature.getProperties();
switch (figureTypeId) {
case 1: // Ellipse
return processEllipseFeature(geometry as Polygon, scaling, mapCenter);
case 3: // Custom polygon
return processPolygonFeature(geometry as Polygon, scaling, mapCenter);
case 4: // Rotated rectangle
return processRectangleFeature(geometry as Polygon, scaling, mapCenter, properties.angle);
default:
throw new Error(`Unsupported figure type: ${figureTypeId}`);
}
}
function processEllipseFeature(geometry: Polygon, scaling: number, mapCenter: Coordinate): Partial<IFigure> {
// Get the extent of the ellipse
const extent = geometry.getExtent();
// Calculate dimensions
const width = (extent[2] - extent[0]) / scaling;
const height = (extent[3] - extent[1]) / scaling;
// Calculate center in map coordinates
const centerX = (extent[0] + extent[2]) / 2;
const centerY = (extent[1] + extent[3]) / 2;
// Convert to database coordinates
const left = (centerX - mapCenter[0]) / scaling;
const top = (mapCenter[1] - centerY) / scaling;
return {
figure_type_id: 1,
width: parseFloat(width.toFixed(6)),
height: parseFloat(height.toFixed(6)),
left: parseFloat(left.toFixed(6)),
top: parseFloat(top.toFixed(6))
};
}
function processPolygonFeature(
geometry: Polygon,
scaling: number,
mapCenter: Coordinate,
): Partial<IFigure> {
// Get polygon coordinates (first ring only)
const coordinates = geometry.getCoordinates()[0];
// Calculate the bounding box to get left/top
let minX = Infinity, maxX = -Infinity;
let minY = Infinity, maxY = -Infinity;
coordinates.forEach(coord => {
minX = Math.min(minX, coord[0]);
maxX = Math.max(maxX, coord[0]);
minY = Math.min(minY, coord[1]);
maxY = Math.max(maxY, coord[1]);
});
// Calculate center of the polygon
const centerX = (minX + maxX) / 2;
const centerY = (minY + maxY) / 2;
// Convert center to database coordinates for left/top
const left = (centerX - mapCenter[0]) / scaling;
const top = (mapCenter[1] - centerY) / scaling;
// Now convert all points relative to this center
const pointsArray = coordinates.map(coord => {
// Points are relative to (left, top), which is our calculated center
const relativeX = (coord[0] - mapCenter[0]) / scaling - left;
const relativeY = (mapCenter[1] - coord[1]) / scaling - top;
return `${relativeX.toFixed(6)};${relativeY.toFixed(6)}`;
});
// Join points with space (skip last point if it's duplicate of first)
const points = pointsArray.slice(0, -1).join(' ');
return {
figure_type_id: 3,
points,
left: parseFloat(left.toFixed(6)),
top: parseFloat(top.toFixed(6))
};
}
function processRectangleFeature(
geometry: Polygon,
scaling: number,
mapCenter: Coordinate,
angle: number = 0
): Partial<IFigure> {
// Clone geometry to avoid modifying original
const workingGeometry = geometry.clone();
// Get the current center
const currentCenter = getCenter(workingGeometry.getExtent());
// If there's rotation, we need to unrotate to get axis-aligned bounds
if (angle !== 0) {
workingGeometry.rotate(angle * Math.PI / 180, currentCenter);
}
// Get the unrotated extent
const extent = workingGeometry.getExtent();
// Calculate dimensions from unrotated geometry
const width = (extent[2] - extent[0]) / scaling;
const height = (extent[3] - extent[1]) / scaling;
// Calculate center of unrotated geometry
const unrotatedCenter = getCenter(extent);
// Convert to database coordinates
const left = (unrotatedCenter[0] - mapCenter[0]) / scaling;
const top = (mapCenter[1] - unrotatedCenter[1]) / scaling;
return {
figure_type_id: 4,
width: parseFloat(width.toFixed(6)),
height: parseFloat(height.toFixed(6)),
left: parseFloat(left.toFixed(6)),
top: parseFloat(top.toFixed(6)),
angle: angle // Preserve the angle
};
}
// Helper function to update a feature after movement
export function updateFeatureCoordinates(
feature: Feature,
scaling: number,
mapCenter: Coordinate
): void {
const figureTypeId = feature.get('figure_type_id');
const updatedFigure = processFeatureToFigure(feature, figureTypeId, scaling, mapCenter);
// You can now send updatedFigure to your backend
console.log('Updated figure data:', updatedFigure);
// Example: Update feature properties with database coordinates for reference
feature.set('db_coordinates', updatedFigure);
}
export function processFigure(
figure: IFigure,
scaling: number,
@ -201,6 +418,8 @@ export function processFigure(
type: "Feature",
geometry: new GeoJSON().writeGeometryObject(ellipseGeom),
properties: {
year: figure.year,
figure_type_id: figure.figure_type_id,
type: figure.type,
object_id: figure.object_id,
planning: figure.planning
@ -229,6 +448,8 @@ export function processFigure(
type: "Feature",
geometry: new GeoJSON().writeGeometryObject(polygon),
properties: {
year: figure.year,
figure_type_id: figure.figure_type_id,
type: figure.type,
object_id: figure.object_id,
planning: figure.planning
@ -264,6 +485,8 @@ export function processFigure(
type: "Feature",
geometry: new GeoJSON().writeGeometryObject(geometry1),
properties: {
year: figure.year,
figure_type_id: figure.figure_type_id,
type: figure.type,
object_id: figure.object_id,
planning: figure.planning,
@ -590,9 +813,54 @@ export const addInteractions = (
}
if (currentTool == 'Mover') {
setTranslate(map_id, new Translate({
const translateMode = new Translate({
features: new Collection(getSelectedFeatures(map_id))
}))
})
translateMode.on('translateend', async (e) => {
const features = e.features.getArray()
let changesJSON: any = []
features.map((f: Feature<Geometry>) => {
if (f.get('geometry_type') === 'line') {
const json = new GeoJSON()
changesJSON.push({
object_id: f.get('object_id'),
year: f.get('year'),
type: 'line',
feature: JSON.parse(json.writeFeature(f, {
featureProjection: 'EPSG:3857',
dataProjection: 'EPSG:3857'
}))
})
} else {
const json = new GeoJSON()
changesJSON.push({
object_id: f.get('object_id'),
year: f.get('year'),
type: 'figure',
feature: JSON.parse(json.writeFeature(f, {
featureProjection: 'EPSG:3857',
dataProjection: 'EPSG:3857'
}))
})
}
})
console.log(changesJSON)
await axiosInstance.post(`/gis/features/update`, {
features: changesJSON
},
{
baseURL: import.meta.env.VITE_API_NEST_URL,
})
})
setTranslate(map_id, translateMode)
const translate = getTranslate(map_id)
if (translate) {

View File

@ -4,7 +4,6 @@ import { SatelliteMapsProvider } from "./map";
import Map from "ol/Map";
import { Coordinate } from "ol/coordinate";
import { Mode } from "../store/map";
export interface IRegion {
id: number
name: string
@ -30,7 +29,8 @@ export interface IFigure {
label_size: number | null,
year: number,
type: number,
planning: boolean
planning: boolean,
modified?: string
}
export interface ILine {
@ -49,7 +49,8 @@ export interface ILine {
label_positions: string | null,
year: number,
type: number,
planning: boolean
planning: boolean,
modified?: string
}
export interface ICitySettings {

View File

@ -27,7 +27,6 @@ import { getSelectedRegion, setCurrentObjectId, setSelectedDistrict, setSelected
import View from 'ol/View';
import { getPrintOrientation } from './print';
import { getDistrictsData, getRegionsData } from './regions';
import { fromExtent } from 'ol/geom/Polygon';
export type Mode = 'edit' | 'view' | 'print'
@ -273,7 +272,7 @@ export const initializeMapState = (
condition: noModifierKeys
})
selectionDragBox.on('boxend', (e) => {
selectionDragBox.on('boxend', () => {
const extent = selectionDragBox.getGeometry().getExtent()
const figuresSource = figuresLayer.getSource()