diff --git a/src/pages/MapPage/mapStore.ts b/src/pages/MapPage/mapStore.ts index 4e7bf28..430d574 100644 --- a/src/pages/MapPage/mapStore.ts +++ b/src/pages/MapPage/mapStore.ts @@ -22,6 +22,28 @@ interface ApiSight { 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); @@ -33,25 +55,19 @@ class MapStore { getRoutes = async () => { const routes = await languageInstance("ru").get("/route"); - const routedIds = routes.data.map((route: any) => route.id); - const mappedRoutes: ApiRoute[] = []; for (const routeId of routedIds) { const responseSoloRoute = await languageInstance("ru").get( `/route/${routeId}` ); const route = responseSoloRoute.data; - - const mappedRoute = { + mappedRoutes.push({ id: route.id, route_number: route.route_number, path: route.path, - }; - - mappedRoutes.push(mappedRoute); + }); } - this.routes = mappedRoutes.sort((a, b) => a.route_number.localeCompare(b.route_number) ); @@ -59,27 +75,23 @@ class MapStore { getStations = async () => { const stations = await languageInstance("ru").get("/station"); - const mappedStations = stations.data.map((station: any) => ({ + this.stations = stations.data.map((station: any) => ({ id: station.id, name: station.name, latitude: station.latitude, longitude: station.longitude, })); - - this.stations = mappedStations; }; getSights = async () => { const sights = await languageInstance("ru").get("/sight"); - const mappedSights = sights.data.map((sight: any) => ({ + this.sights = sights.data.map((sight: any) => ({ id: sight.id, name: sight.name, description: sight.description, latitude: sight.latitude, longitude: sight.longitude, })); - - this.sights = mappedSights; }; deleteRecourse = async (recourse: string, id: number) => { @@ -94,31 +106,98 @@ class MapStore { }; handleSave = async (json: string) => { - const sights: any[] = []; - const routes: any[] = []; - const stations: any[] = []; + const newSights: any[] = []; + const newRoutes: any[] = []; + const newStations: any[] = []; + const updatedSights: any[] = []; + const updatedRoutes: any[] = []; + const updatedStations: any[] = []; const parsedJSON = JSON.parse(json); + console.log("Данные для сохранения (GeoJSON):", parsedJSON); - console.log(parsedJSON); - parsedJSON.features.forEach((feature: any) => { + for (const feature of parsedJSON.features) { const { geometry, properties, id } = feature; - const idCanBeSplited = id.split("-"); + const idParts = String(id).split("-"); - if (!(idCanBeSplited.length > 1)) { + 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") { - stations.push({ + newStations.push({ name: properties.name || "", latitude: geometry.coordinates[1], longitude: geometry.coordinates[0], }); } else if (properties.featureType === "route") { - routes.push({ + newRoutes.push({ route_number: properties.name || "", path: geometry.coordinates, }); } else if (properties.featureType === "sight") { - sights.push({ + newSights.push({ name: properties.name || "", description: properties.description || "", latitude: geometry.coordinates[1], @@ -126,16 +205,54 @@ class MapStore { }); } } - }); + } - for (const station of stations) { - await languageInstance("ru").post("/station", station); + const requests: Promise[] = []; + + console.log( + `К созданию: ${newStations.length} станций, ${newRoutes.length} маршрутов, ${newSights.length} достопримечательностей.` + ); + console.log( + `К обновлению: ${updatedStations.length} станций, ${updatedRoutes.length} маршрутов, ${updatedSights.length} достопримечательностей.` + ); + + 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) { + console.log("Нет изменений для сохранения."); + return; } - for (const route of routes) { - await languageInstance("ru").post("/route", route); - } - for (const sight of sights) { - await languageInstance("ru").post("/sight", sight); + + try { + await Promise.all(requests); + console.log("Все изменения успешно сохранены!"); + + await Promise.all([ + this.getRoutes(), + this.getStations(), + this.getSights(), + ]); + } catch (error) { + console.error("Ошибка при сохранении данных:", error); + throw error; } }; }