253 lines
7.8 KiB
TypeScript
253 lines
7.8 KiB
TypeScript
import { languageInstance } from "@shared";
|
||
import { makeAutoObservable } from "mobx";
|
||
|
||
interface ApiRoute {
|
||
id: number;
|
||
route_number: string;
|
||
path: [number, number][];
|
||
}
|
||
|
||
interface ApiStation {
|
||
id: number;
|
||
name: string;
|
||
latitude: number;
|
||
longitude: number;
|
||
}
|
||
|
||
interface ApiSight {
|
||
id: number;
|
||
name: string;
|
||
description: string;
|
||
latitude: number;
|
||
longitude: number;
|
||
}
|
||
|
||
// Допуск для сравнения координат, чтобы избежать ошибок с точностью чисел.
|
||
const COORDINATE_PRECISION_TOLERANCE = 1e-9;
|
||
|
||
// Вспомогательная функция, обновленная для сравнения с допуском.
|
||
const arePathsEqual = (
|
||
path1: [number, number][],
|
||
path2: [number, number][]
|
||
): boolean => {
|
||
if (path1.length !== path2.length) {
|
||
return false;
|
||
}
|
||
for (let i = 0; i < path1.length; i++) {
|
||
if (
|
||
Math.abs(path1[i][0] - path2[i][0]) > COORDINATE_PRECISION_TOLERANCE ||
|
||
Math.abs(path1[i][1] - path2[i][1]) > COORDINATE_PRECISION_TOLERANCE
|
||
) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
};
|
||
|
||
class MapStore {
|
||
constructor() {
|
||
makeAutoObservable(this);
|
||
}
|
||
|
||
routes: ApiRoute[] = [];
|
||
stations: ApiStation[] = [];
|
||
sights: ApiSight[] = [];
|
||
|
||
getRoutes = async () => {
|
||
const response = await languageInstance("ru").get("/route");
|
||
|
||
const routesIds = response.data.map((route: any) => route.id);
|
||
for (const id of routesIds) {
|
||
const route = await languageInstance("ru").get(`/route/${id}`);
|
||
this.routes.push({
|
||
id: route.data.id,
|
||
route_number: route.data.route_number,
|
||
path: route.data.path,
|
||
});
|
||
}
|
||
const mappedRoutes: ApiRoute[] = response.data.map((route: any) => ({
|
||
id: route.id,
|
||
route_number: route.route_number,
|
||
path: route.path,
|
||
}));
|
||
this.routes = mappedRoutes.sort((a, b) =>
|
||
a.route_number.localeCompare(b.route_number)
|
||
);
|
||
};
|
||
|
||
getStations = async () => {
|
||
const stations = await languageInstance("ru").get("/station");
|
||
this.stations = stations.data.map((station: any) => ({
|
||
id: station.id,
|
||
name: station.name,
|
||
latitude: station.latitude,
|
||
longitude: station.longitude,
|
||
}));
|
||
};
|
||
|
||
getSights = async () => {
|
||
const sights = await languageInstance("ru").get("/sight");
|
||
this.sights = sights.data.map((sight: any) => ({
|
||
id: sight.id,
|
||
name: sight.name,
|
||
description: sight.description,
|
||
latitude: sight.latitude,
|
||
longitude: sight.longitude,
|
||
}));
|
||
};
|
||
|
||
deleteRecourse = async (recourse: string, id: number) => {
|
||
await languageInstance("ru").delete(`/${recourse}/${id}`);
|
||
if (recourse === "route") {
|
||
this.routes = this.routes.filter((route) => route.id !== id);
|
||
} else if (recourse === "station") {
|
||
this.stations = this.stations.filter((station) => station.id !== id);
|
||
} else if (recourse === "sight") {
|
||
this.sights = this.sights.filter((sight) => sight.id !== id);
|
||
}
|
||
};
|
||
|
||
handleSave = async (json: string) => {
|
||
const newSights: any[] = [];
|
||
const newRoutes: any[] = [];
|
||
const newStations: any[] = [];
|
||
const updatedSights: any[] = [];
|
||
const updatedRoutes: any[] = [];
|
||
const updatedStations: any[] = [];
|
||
|
||
const parsedJSON = JSON.parse(json);
|
||
|
||
for (const feature of parsedJSON.features) {
|
||
const { geometry, properties, id } = feature;
|
||
const idParts = String(id).split("-");
|
||
|
||
if (idParts.length > 1) {
|
||
const featureType = idParts[0];
|
||
const numericId = parseInt(idParts[1], 10);
|
||
if (isNaN(numericId)) continue;
|
||
|
||
if (featureType === "station") {
|
||
const originalStation = this.stations.find((s) => s.id === numericId);
|
||
if (!originalStation) continue;
|
||
|
||
const currentStation = {
|
||
name: properties.name || "",
|
||
latitude: geometry.coordinates[1],
|
||
longitude: geometry.coordinates[0],
|
||
};
|
||
|
||
// ИЗМЕНЕНИЕ: Сравнение координат с допуском
|
||
if (
|
||
originalStation.name !== currentStation.name ||
|
||
Math.abs(originalStation.latitude - currentStation.latitude) >
|
||
COORDINATE_PRECISION_TOLERANCE ||
|
||
Math.abs(originalStation.longitude - currentStation.longitude) >
|
||
COORDINATE_PRECISION_TOLERANCE
|
||
) {
|
||
updatedStations.push({ id: numericId, ...currentStation });
|
||
}
|
||
} else if (featureType === "route") {
|
||
const originalRoute = this.routes.find((r) => r.id === numericId);
|
||
if (!originalRoute) continue;
|
||
|
||
const currentRoute = {
|
||
route_number: properties.name || "",
|
||
path: geometry.coordinates,
|
||
};
|
||
|
||
// ИЗМЕНЕНИЕ: Используется новая функция arePathsEqual с допуском
|
||
if (
|
||
originalRoute.route_number !== currentRoute.route_number ||
|
||
!arePathsEqual(originalRoute.path, currentRoute.path)
|
||
) {
|
||
updatedRoutes.push({ id: numericId, ...currentRoute });
|
||
}
|
||
} else if (featureType === "sight") {
|
||
const originalSight = this.sights.find((s) => s.id === numericId);
|
||
if (!originalSight) continue;
|
||
|
||
const currentSight = {
|
||
name: properties.name || "",
|
||
description: properties.description || "",
|
||
latitude: geometry.coordinates[1],
|
||
longitude: geometry.coordinates[0],
|
||
};
|
||
|
||
// ИЗМЕНЕНИЕ: Сравнение координат с допуском
|
||
if (
|
||
originalSight.name !== currentSight.name ||
|
||
originalSight.description !== currentSight.description ||
|
||
Math.abs(originalSight.latitude - currentSight.latitude) >
|
||
COORDINATE_PRECISION_TOLERANCE ||
|
||
Math.abs(originalSight.longitude - currentSight.longitude) >
|
||
COORDINATE_PRECISION_TOLERANCE
|
||
) {
|
||
updatedSights.push({ id: numericId, ...currentSight });
|
||
}
|
||
}
|
||
} else {
|
||
if (properties.featureType === "station") {
|
||
newStations.push({
|
||
name: properties.name || "",
|
||
latitude: geometry.coordinates[1],
|
||
longitude: geometry.coordinates[0],
|
||
});
|
||
} else if (properties.featureType === "route") {
|
||
newRoutes.push({
|
||
route_number: properties.name || "",
|
||
path: geometry.coordinates,
|
||
});
|
||
} else if (properties.featureType === "sight") {
|
||
newSights.push({
|
||
name: properties.name || "",
|
||
description: properties.description || "",
|
||
latitude: geometry.coordinates[1],
|
||
longitude: geometry.coordinates[0],
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
const requests: Promise<any>[] = [];
|
||
|
||
newStations.forEach((data) =>
|
||
requests.push(languageInstance("ru").post("/station", data))
|
||
);
|
||
newRoutes.forEach((data) =>
|
||
requests.push(languageInstance("ru").post("/route", data))
|
||
);
|
||
newSights.forEach((data) =>
|
||
requests.push(languageInstance("ru").post("/sight", data))
|
||
);
|
||
|
||
updatedStations.forEach(({ id, ...data }) =>
|
||
requests.push(languageInstance("ru").patch(`/station/${id}`, data))
|
||
);
|
||
updatedRoutes.forEach(({ id, ...data }) =>
|
||
requests.push(languageInstance("ru").patch(`/route/${id}`, data))
|
||
);
|
||
updatedSights.forEach(({ id, ...data }) =>
|
||
requests.push(languageInstance("ru").patch(`/sight/${id}`, data))
|
||
);
|
||
|
||
if (requests.length === 0) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await Promise.all(requests);
|
||
|
||
await Promise.all([
|
||
this.getRoutes(),
|
||
this.getStations(),
|
||
this.getSights(),
|
||
]);
|
||
} catch (error) {
|
||
console.error("Ошибка при сохранении данных:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
}
|
||
|
||
export default new MapStore();
|