moving features: write to sqlite modified features
This commit is contained in:
@ -22,6 +22,7 @@ import VectorImageLayer from "ol/layer/VectorImage";
|
|||||||
import VectorSource from "ol/source/Vector";
|
import VectorSource from "ol/source/Vector";
|
||||||
import Map from "ol/Map";
|
import Map from "ol/Map";
|
||||||
import { Icon, Style } from "ol/style";
|
import { Icon, Style } from "ol/style";
|
||||||
|
import axiosInstance from "../../http/axiosInstanceNest";
|
||||||
|
|
||||||
export function getCitySettings() {
|
export function getCitySettings() {
|
||||||
return {
|
return {
|
||||||
@ -94,11 +95,26 @@ export const addFigures = (
|
|||||||
if (figuresData.length > 0) {
|
if (figuresData.length > 0) {
|
||||||
const geoJsonObject = {
|
const geoJsonObject = {
|
||||||
type: "FeatureCollection",
|
type: "FeatureCollection",
|
||||||
features: figuresData.map((figure: IFigure) => processFigure(
|
features: figuresData.map((figure: IFigure) => {
|
||||||
figure,
|
if (figure.modified) {
|
||||||
settings.scale,
|
console.log("found modified", JSON.parse(figure.modified))
|
||||||
[center[0], center[1]]
|
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)
|
const features = new GeoJSON().readFeatures(geoJsonObject)
|
||||||
@ -127,7 +143,22 @@ export const addLines = (
|
|||||||
if (linesData.length > 0) {
|
if (linesData.length > 0) {
|
||||||
const geoJsonObject = {
|
const geoJsonObject = {
|
||||||
type: "FeatureCollection",
|
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)
|
const features = new GeoJSON().readFeatures(geoJsonObject)
|
||||||
@ -144,6 +175,39 @@ export const calculateAngle = (coords: [number, number][]) => {
|
|||||||
return Math.atan2(dy, dx); // Angle in radians
|
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(
|
export function processLine(
|
||||||
line: ILine,
|
line: ILine,
|
||||||
scaling: number,
|
scaling: number,
|
||||||
@ -167,6 +231,7 @@ export function processLine(
|
|||||||
type: "Feature",
|
type: "Feature",
|
||||||
geometry: new GeoJSON().writeGeometryObject(geometry),
|
geometry: new GeoJSON().writeGeometryObject(geometry),
|
||||||
properties: {
|
properties: {
|
||||||
|
year: line.year,
|
||||||
type: line.type,
|
type: line.type,
|
||||||
geometry_type: 'line',
|
geometry_type: 'line',
|
||||||
object_id: line.object_id,
|
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(
|
export function processFigure(
|
||||||
figure: IFigure,
|
figure: IFigure,
|
||||||
scaling: number,
|
scaling: number,
|
||||||
@ -201,6 +418,8 @@ export function processFigure(
|
|||||||
type: "Feature",
|
type: "Feature",
|
||||||
geometry: new GeoJSON().writeGeometryObject(ellipseGeom),
|
geometry: new GeoJSON().writeGeometryObject(ellipseGeom),
|
||||||
properties: {
|
properties: {
|
||||||
|
year: figure.year,
|
||||||
|
figure_type_id: figure.figure_type_id,
|
||||||
type: figure.type,
|
type: figure.type,
|
||||||
object_id: figure.object_id,
|
object_id: figure.object_id,
|
||||||
planning: figure.planning
|
planning: figure.planning
|
||||||
@ -229,6 +448,8 @@ export function processFigure(
|
|||||||
type: "Feature",
|
type: "Feature",
|
||||||
geometry: new GeoJSON().writeGeometryObject(polygon),
|
geometry: new GeoJSON().writeGeometryObject(polygon),
|
||||||
properties: {
|
properties: {
|
||||||
|
year: figure.year,
|
||||||
|
figure_type_id: figure.figure_type_id,
|
||||||
type: figure.type,
|
type: figure.type,
|
||||||
object_id: figure.object_id,
|
object_id: figure.object_id,
|
||||||
planning: figure.planning
|
planning: figure.planning
|
||||||
@ -264,6 +485,8 @@ export function processFigure(
|
|||||||
type: "Feature",
|
type: "Feature",
|
||||||
geometry: new GeoJSON().writeGeometryObject(geometry1),
|
geometry: new GeoJSON().writeGeometryObject(geometry1),
|
||||||
properties: {
|
properties: {
|
||||||
|
year: figure.year,
|
||||||
|
figure_type_id: figure.figure_type_id,
|
||||||
type: figure.type,
|
type: figure.type,
|
||||||
object_id: figure.object_id,
|
object_id: figure.object_id,
|
||||||
planning: figure.planning,
|
planning: figure.planning,
|
||||||
@ -590,9 +813,54 @@ export const addInteractions = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentTool == 'Mover') {
|
if (currentTool == 'Mover') {
|
||||||
setTranslate(map_id, new Translate({
|
const translateMode = new Translate({
|
||||||
features: new Collection(getSelectedFeatures(map_id))
|
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)
|
const translate = getTranslate(map_id)
|
||||||
if (translate) {
|
if (translate) {
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { SatelliteMapsProvider } from "./map";
|
|||||||
import Map from "ol/Map";
|
import Map from "ol/Map";
|
||||||
import { Coordinate } from "ol/coordinate";
|
import { Coordinate } from "ol/coordinate";
|
||||||
import { Mode } from "../store/map";
|
import { Mode } from "../store/map";
|
||||||
|
|
||||||
export interface IRegion {
|
export interface IRegion {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
@ -30,7 +29,8 @@ export interface IFigure {
|
|||||||
label_size: number | null,
|
label_size: number | null,
|
||||||
year: number,
|
year: number,
|
||||||
type: number,
|
type: number,
|
||||||
planning: boolean
|
planning: boolean,
|
||||||
|
modified?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILine {
|
export interface ILine {
|
||||||
@ -49,7 +49,8 @@ export interface ILine {
|
|||||||
label_positions: string | null,
|
label_positions: string | null,
|
||||||
year: number,
|
year: number,
|
||||||
type: number,
|
type: number,
|
||||||
planning: boolean
|
planning: boolean,
|
||||||
|
modified?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICitySettings {
|
export interface ICitySettings {
|
||||||
|
|||||||
@ -27,7 +27,6 @@ import { getSelectedRegion, setCurrentObjectId, setSelectedDistrict, setSelected
|
|||||||
import View from 'ol/View';
|
import View from 'ol/View';
|
||||||
import { getPrintOrientation } from './print';
|
import { getPrintOrientation } from './print';
|
||||||
import { getDistrictsData, getRegionsData } from './regions';
|
import { getDistrictsData, getRegionsData } from './regions';
|
||||||
import { fromExtent } from 'ol/geom/Polygon';
|
|
||||||
|
|
||||||
export type Mode = 'edit' | 'view' | 'print'
|
export type Mode = 'edit' | 'view' | 'print'
|
||||||
|
|
||||||
@ -273,7 +272,7 @@ export const initializeMapState = (
|
|||||||
condition: noModifierKeys
|
condition: noModifierKeys
|
||||||
})
|
})
|
||||||
|
|
||||||
selectionDragBox.on('boxend', (e) => {
|
selectionDragBox.on('boxend', () => {
|
||||||
const extent = selectionDragBox.getGeometry().getExtent()
|
const extent = selectionDragBox.getGeometry().getExtent()
|
||||||
|
|
||||||
const figuresSource = figuresLayer.getSource()
|
const figuresSource = figuresLayer.getSource()
|
||||||
|
|||||||
BIN
server/ems.db
BIN
server/ems.db
Binary file not shown.
8
server/src/gis/dto/update-features-batch.ts
Normal file
8
server/src/gis/dto/update-features-batch.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsArray } from "class-validator";
|
||||||
|
|
||||||
|
export class UpdateFeaturesBatchDto {
|
||||||
|
@ApiProperty()
|
||||||
|
@IsArray()
|
||||||
|
features: any[]
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import { Body, Controller, Get, Param, ParseIntPipe, Post, Query } from '@nestjs
|
|||||||
import { GisService } from './gis.service';
|
import { GisService } from './gis.service';
|
||||||
import { ApiBody } from '@nestjs/swagger';
|
import { ApiBody } from '@nestjs/swagger';
|
||||||
import { BoundsRequestDto } from './dto/bound';
|
import { BoundsRequestDto } from './dto/bound';
|
||||||
|
import { UpdateFeaturesBatchDto } from './dto/update-features-batch';
|
||||||
|
|
||||||
@Controller('gis')
|
@Controller('gis')
|
||||||
export class GisController {
|
export class GisController {
|
||||||
@ -22,6 +23,11 @@ export class GisController {
|
|||||||
return await this.gisService.getBoundsByEntityTypeAndId(entity_type, entity_id)
|
return await this.gisService.getBoundsByEntityTypeAndId(entity_type, entity_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('/features/update')
|
||||||
|
async updateFeaturesBatch(@Body() updateFeaturesBatch: UpdateFeaturesBatchDto) {
|
||||||
|
return await this.gisService.updateFeaturesBatch(updateFeaturesBatch.features)
|
||||||
|
}
|
||||||
|
|
||||||
@Post('/bounds/:entity_type')
|
@Post('/bounds/:entity_type')
|
||||||
@ApiBody({ type: BoundsRequestDto })
|
@ApiBody({ type: BoundsRequestDto })
|
||||||
async getBoundsByEntityTypeAndList(
|
async getBoundsByEntityTypeAndList(
|
||||||
|
|||||||
@ -119,26 +119,108 @@ export class GisService {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// async getFigures(offset: number, limit: number, year: number, city_id: number): Promise<any[]> {
|
||||||
|
// const result = await this.emsDataSource.query(`
|
||||||
|
// SELECT o.*, f.[figure_type_id], f.[left], f.[top], f.[width], f.[height], f.[angle], f.[points], f.[label_left], f.[label_top], f.[label_angle], f.[label_size] FROM New_Gis..figures f
|
||||||
|
// JOIN nGeneral..vObjects o ON o.object_id = f.object_id WHERE o.id_city = ${city_id} AND f.year = ${year}
|
||||||
|
// ORDER BY f.year
|
||||||
|
// OFFSET ${Number(offset) || 0} ROWS
|
||||||
|
// FETCH NEXT ${Number(limit) || 10} ROWS ONLY;
|
||||||
|
// `)
|
||||||
|
// return result
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async getLines(year: number, city_id: number): Promise<any[]> {
|
||||||
|
// const result = await this.emsDataSource.query(
|
||||||
|
// `
|
||||||
|
// SELECT o.[object_id], o.[id_city], o.[id_parent], o.[type], o.[planning], o.[activity], o.[kvr], o.[jur], o.[fuel], o.[boiler_id],
|
||||||
|
// l.[x1], l.[y1], l.[x2], l.[y2], l.[points], l.[label_offset], l.[group_id], l.[show_label], l.[forced_lengths], l.[label_sizes], l.[label_angles], l.[label_positions], l.[year]
|
||||||
|
// FROM New_Gis..lines l
|
||||||
|
// JOIN nGeneral..vObjects o ON l.object_id = o.object_id WHERE o.id_city = ${city_id} AND l.year = ${year};
|
||||||
|
// `
|
||||||
|
// )
|
||||||
|
// return result
|
||||||
|
// }
|
||||||
|
|
||||||
async getFigures(offset: number, limit: number, year: number, city_id: number): Promise<any[]> {
|
async getFigures(offset: number, limit: number, year: number, city_id: number): Promise<any[]> {
|
||||||
const result = await this.emsDataSource.query(`
|
// Get original figures from EMS
|
||||||
SELECT o.*, f.[figure_type_id], f.[left], f.[top], f.[width], f.[height], f.[angle], f.[points], f.[label_left], f.[label_top], f.[label_angle], f.[label_size] FROM New_Gis..figures f
|
const originalFigures = await this.emsDataSource.query(`
|
||||||
JOIN nGeneral..vObjects o ON o.object_id = f.object_id WHERE o.id_city = ${city_id} AND f.year = ${year}
|
SELECT o.[object_id], o.[id_city], o.[id_parent], o.[type], o.[planning], o.[activity], o.[kvr], o.[jur], o.[fuel], o.[boiler_id], f.[figure_type_id], f.[left], f.[top], f.[width], f.[height], f.[angle], f.[points], f.[label_left], f.[label_top], f.[label_angle], f.[label_size], f.[year] FROM New_Gis..figures f
|
||||||
ORDER BY f.year
|
JOIN nGeneral..vObjects o ON o.object_id = f.object_id WHERE o.id_city = ${city_id} AND f.year = ${year}
|
||||||
OFFSET ${Number(offset) || 0} ROWS
|
ORDER BY f.year
|
||||||
FETCH NEXT ${Number(limit) || 10} ROWS ONLY;
|
OFFSET ${Number(offset) || 0} ROWS
|
||||||
`)
|
FETCH NEXT ${Number(limit) || 10} ROWS ONLY;
|
||||||
return result
|
`)
|
||||||
|
|
||||||
|
// Get modified figures from SQLite for the same year
|
||||||
|
const modifiedFigures = await this.dataSource.query(
|
||||||
|
`SELECT * FROM figures WHERE year = ?`,
|
||||||
|
[year]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a lookup map using object_id + year as key
|
||||||
|
const modifiedMap = new Map()
|
||||||
|
modifiedFigures.forEach(fig => {
|
||||||
|
const key = `${fig.object_id}_${fig.year}`
|
||||||
|
modifiedMap.set(key, fig)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace original values with modified ones
|
||||||
|
const mergedResult = originalFigures.map(original => {
|
||||||
|
const key = `${original.object_id}_${year}`
|
||||||
|
const modified = modifiedMap.get(key)
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
modified: modified.feature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return original
|
||||||
|
})
|
||||||
|
|
||||||
|
return mergedResult
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLines(year: number, city_id: number): Promise<any[]> {
|
async getLines(year: number, city_id: number): Promise<any[]> {
|
||||||
const result = await this.emsDataSource.query(
|
// Get original lines from EMS
|
||||||
`
|
const originalLines = await this.emsDataSource.query(`
|
||||||
SELECT * FROM New_Gis..lines l
|
SELECT o.[object_id], o.[id_city], o.[id_parent], o.[type], o.[planning], o.[activity], o.[kvr], o.[jur], o.[fuel], o.[boiler_id],
|
||||||
JOIN nGeneral..vObjects o ON l.object_id = o.object_id WHERE o.id_city = ${city_id} AND l.year = ${year};
|
l.[x1], l.[y1], l.[x2], l.[y2], l.[points], l.[label_offset], l.[group_id], l.[show_label], l.[forced_lengths], l.[label_sizes], l.[label_angles], l.[label_positions], l.[year]
|
||||||
`
|
FROM New_Gis..lines l
|
||||||
|
JOIN nGeneral..vObjects o ON l.object_id = o.object_id WHERE o.id_city = ${city_id} AND l.year = ${year};
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Get modified lines from SQLite for the same year
|
||||||
|
const modifiedLines = await this.dataSource.query(
|
||||||
|
`SELECT * FROM lines WHERE year = ?`,
|
||||||
|
[year]
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
// Create lookup map with object_id + year
|
||||||
|
const modifiedMap = new Map()
|
||||||
|
modifiedLines.forEach(line => {
|
||||||
|
const key = `${line.object_id}_${line.year}`
|
||||||
|
modifiedMap.set(key, line)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace original with modified
|
||||||
|
const mergedResult = originalLines.map(original => {
|
||||||
|
const key = `${original.object_id}_${year}`
|
||||||
|
const modified = modifiedMap.get(key)
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
modified: modified.feature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return original
|
||||||
|
})
|
||||||
|
|
||||||
|
return mergedResult
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRegionBorders(): Promise<any[]> {
|
async getRegionBorders(): Promise<any[]> {
|
||||||
@ -150,4 +232,63 @@ export class GisService {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateFeaturesBatch(features: any[]) {
|
||||||
|
let figures: any[] = []
|
||||||
|
let lines: any[] = []
|
||||||
|
|
||||||
|
features.map(feature => {
|
||||||
|
if (feature.type === 'figure') {
|
||||||
|
figures.push(feature)
|
||||||
|
} else if (feature.type === 'line') {
|
||||||
|
lines.push(feature)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Figures to update:', figures.length)
|
||||||
|
console.log('Lines to update:', lines.length)
|
||||||
|
|
||||||
|
// Update figures
|
||||||
|
if (figures.length > 0) {
|
||||||
|
const figurePlaceholders = figures.map(() =>
|
||||||
|
`(?, ?, ?)`
|
||||||
|
).join(', ')
|
||||||
|
|
||||||
|
const figureParams = figures.flatMap(fig => [
|
||||||
|
fig.object_id,
|
||||||
|
fig.year, // Default year if not provided
|
||||||
|
JSON.stringify(fig.feature)
|
||||||
|
])
|
||||||
|
|
||||||
|
await this.dataSource.query(`
|
||||||
|
INSERT OR REPLACE INTO figures
|
||||||
|
(object_id, year, feature)
|
||||||
|
VALUES ${figurePlaceholders}
|
||||||
|
`, figureParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update lines
|
||||||
|
if (lines.length > 0) {
|
||||||
|
const linePlaceholders = lines.map(() =>
|
||||||
|
`(?, ?, ?)`
|
||||||
|
).join(', ')
|
||||||
|
|
||||||
|
const lineParams = lines.flatMap(line => [
|
||||||
|
line.object_id,
|
||||||
|
line.year,
|
||||||
|
JSON.stringify(line.feature)
|
||||||
|
])
|
||||||
|
|
||||||
|
await this.dataSource.query(`
|
||||||
|
INSERT OR REPLACE INTO lines
|
||||||
|
(object_id, year, feature)
|
||||||
|
VALUES ${linePlaceholders}
|
||||||
|
`, lineParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Updated ${figures.length} figures and ${lines.length} lines`
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,14 @@ import { NestFactory } from '@nestjs/core';
|
|||||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import { ValidationPipe } from '@nestjs/common';
|
import { ValidationPipe } from '@nestjs/common';
|
||||||
|
import { json } from 'express';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule, {
|
const app = await NestFactory.create(AppModule, {
|
||||||
cors: true
|
cors: true
|
||||||
});
|
});
|
||||||
app.enableCors()
|
app.enableCors()
|
||||||
|
app.use(json({ limit: '50mb' }))
|
||||||
app.useGlobalPipes(new ValidationPipe({ transform: true }))
|
app.useGlobalPipes(new ValidationPipe({ transform: true }))
|
||||||
const config = new DocumentBuilder()
|
const config = new DocumentBuilder()
|
||||||
.setTitle('Fuel API')
|
.setTitle('Fuel API')
|
||||||
|
|||||||
Reference in New Issue
Block a user