fix: fix build errors
This commit is contained in:
4
src/client/src/App.d.ts
vendored
Normal file
4
src/client/src/App.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
declare const App: React.FC;
|
||||||
|
export default App;
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
GetCityResponse,
|
GetCityResponse,
|
||||||
GetSightArticleResponse,
|
GetSightArticleResponse,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
// @ts-ignore
|
||||||
import { orderStationsByRoute } from "../../utils/routeStationsUtils";
|
import { orderStationsByRoute } from "../../utils/routeStationsUtils";
|
||||||
|
|
||||||
class ApiStore {
|
class ApiStore {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export type GetContextResponse = {
|
|||||||
};
|
};
|
||||||
endStopId: string;
|
endStopId: string;
|
||||||
nearestSightId: string;
|
nearestSightId: string;
|
||||||
|
nearestStationId?: string | null;
|
||||||
rawCoordinates: {
|
rawCoordinates: {
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
@@ -105,6 +106,7 @@ export type GetRouteSightsResponse = {
|
|||||||
icon?: string;
|
icon?: string;
|
||||||
alt_icon?: string;
|
alt_icon?: string;
|
||||||
is_default_icon?: boolean;
|
is_default_icon?: boolean;
|
||||||
|
short_name?: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
export type GetRouteStationsResponse = {
|
export type GetRouteStationsResponse = {
|
||||||
|
|||||||
9
src/client/src/api/apiConfig.d.ts
vendored
Normal file
9
src/client/src/api/apiConfig.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { AxiosInstance } from "axios";
|
||||||
|
|
||||||
|
export declare const apiBaseURL: string;
|
||||||
|
export declare const geoBaseURL: string;
|
||||||
|
export declare const weatherBaseURL: string;
|
||||||
|
export declare const getMediaUrl: (id: string) => string;
|
||||||
|
export declare const apiInstance: AxiosInstance;
|
||||||
|
export declare const geoInstance: AxiosInstance;
|
||||||
|
export declare const weatherInstance: AxiosInstance;
|
||||||
8
src/client/src/assets/Constants.d.ts
vendored
Normal file
8
src/client/src/assets/Constants.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export declare const UP_SCALE: number;
|
||||||
|
export declare const PATH_WIDTH: number;
|
||||||
|
export declare const STATION_RADIUS: number;
|
||||||
|
export declare const STATION_OUTLINE_WIDTH: number;
|
||||||
|
export declare const SIGHT_SIZE: number;
|
||||||
|
export declare const SCALE_FACTOR: number;
|
||||||
|
export declare const BACKGROUND_COLOR: number;
|
||||||
|
export declare const PATH_COLOR: number;
|
||||||
13
src/client/src/components/OverlayScrollbarsWrapper.d.ts
vendored
Normal file
13
src/client/src/components/OverlayScrollbarsWrapper.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export declare const OverlayScrollbarsWrapper: React.ForwardRefExoticComponent<
|
||||||
|
React.PropsWithChildren<{
|
||||||
|
className?: string;
|
||||||
|
onScroll?: (event: Event) => void;
|
||||||
|
overflowX?: string;
|
||||||
|
overflowY?: string;
|
||||||
|
scrollbarVisibility?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}> &
|
||||||
|
React.RefAttributes<HTMLElement>
|
||||||
|
>;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import "react";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import rehypeRaw from "rehype-raw";
|
import rehypeRaw from "rehype-raw";
|
||||||
import "./ReactMarkdown.css";
|
import "./ReactMarkdown.css";
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { FederatedPointerEvent, FederatedWheelEvent } from "pixi.js";
|
import { FederatedPointerEvent, FederatedWheelEvent } from "pixi.js";
|
||||||
import { ReactNode, useEffect, useState, useRef, useCallback } from "react";
|
import { ReactNode, useEffect, useState, useRef } from "react";
|
||||||
import { useTransform } from "./transformContext";
|
import { useTransform } from "./transformContext";
|
||||||
import { BACKGROUND_COLOR, SCALE_FACTOR } from "../../assets/Constants";
|
import { BACKGROUND_COLOR, SCALE_FACTOR } from "../../assets/Constants";
|
||||||
import { useApplication } from "@pixi/react";
|
import { useApplication } from "@pixi/react";
|
||||||
import { useGeolocation } from "../../context/GeolocationContext";
|
|
||||||
import ContentAPI from "../../api/content/content.api";
|
|
||||||
import React from "react";
|
|
||||||
import { useGeolocationStore } from "../../stores/hooks/useGeolocationStore";
|
|
||||||
import { useCameraAnimationStore } from "../../stores";
|
import { useCameraAnimationStore } from "../../stores";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
@@ -25,7 +21,6 @@ export const InfiniteCanvas = observer(
|
|||||||
setIsAutoMode,
|
setIsAutoMode,
|
||||||
userActivityTimestamp,
|
userActivityTimestamp,
|
||||||
updateUserActivity,
|
updateUserActivity,
|
||||||
autoModeStartTimestamp,
|
|
||||||
setAutoModeStartTimestamp,
|
setAutoModeStartTimestamp,
|
||||||
} = useTransform();
|
} = useTransform();
|
||||||
const [loaded, setLoaded] = useState(false);
|
const [loaded, setLoaded] = useState(false);
|
||||||
@@ -49,17 +44,14 @@ export const InfiniteCanvas = observer(
|
|||||||
position: { x: number; y: number };
|
position: { x: number; y: number };
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
// Keep these for backward compatibility, but we'll use pinchStartData for calculations
|
// Keep these for backward compatibility, but we'll use pinchStartData for calculations
|
||||||
const [initialPinchDistance, setInitialPinchDistance] = useState<
|
const [, setInitialPinchDistance] = useState<number | null>(null);
|
||||||
number | null
|
const [, setInitialPinchMidpoint] = useState<{
|
||||||
>(null);
|
|
||||||
const [initialPinchMidpoint, setInitialPinchMidpoint] = useState<{
|
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
const [scaleMin, setScaleMin] = useState(0.1); // Default min scale
|
const [scaleMin, setScaleMin] = useState(0.1); // Default min scale
|
||||||
const [scaleMax, setScaleMax] = useState(3); // Default max scale
|
const [scaleMax, setScaleMax] = useState(3); // Default max scale
|
||||||
const store = useGeolocationStore();
|
|
||||||
const cameraAnimationStore = useCameraAnimationStore();
|
const cameraAnimationStore = useCameraAnimationStore();
|
||||||
|
|
||||||
// Add debounced version of syncState to reduce jittering
|
// Add debounced version of syncState to reduce jittering
|
||||||
@@ -269,13 +261,14 @@ export const InfiniteCanvas = observer(
|
|||||||
setInitialPinchMidpoint(null);
|
setInitialPinchMidpoint(null);
|
||||||
pinchStartData.current = null;
|
pinchStartData.current = null;
|
||||||
|
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
const newPosition = {
|
const newPosition = {
|
||||||
x: startPosition.x - startMousePosition.x + e.globalX,
|
x: startPosition.x - startMousePosition.x + e.globalX,
|
||||||
y: startPosition.y - startMousePosition.y + e.globalY,
|
y: startPosition.y - startMousePosition.y + e.globalY,
|
||||||
};
|
};
|
||||||
setPosition(newPosition);
|
setPosition(newPosition);
|
||||||
syncStateDebounced(newPosition, scale);
|
syncStateDebounced(newPosition, scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,31 +1,20 @@
|
|||||||
import React, {
|
import {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
|
||||||
useState,
|
useState,
|
||||||
useMemo,
|
useMemo,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Application, extend } from "@pixi/react";
|
import { extend } from "@pixi/react";
|
||||||
import { Container, Graphics, Sprite, Text } from "pixi.js";
|
import { Container, Graphics, Sprite, Text } from "pixi.js";
|
||||||
import { MapDataProvider, useMapData } from "./MapDataContext";
|
import { MapDataProvider, useMapData } from "./MapDataContext";
|
||||||
import { TransformProvider, useTransform } from "./transformContext";
|
import { TransformProvider, useTransform } from "./transformContext";
|
||||||
import { InfiniteCanvas } from "./InfiniteCanvas";
|
// @ts-ignore
|
||||||
import { TravelPath } from "./TravelPath";
|
|
||||||
import { Station } from "./Station";
|
|
||||||
import { SightsLayer } from "./Sight";
|
|
||||||
import Loader from "../Loader";
|
import Loader from "../Loader";
|
||||||
import {
|
import { UP_SCALE } from "./Constants";
|
||||||
BACKGROUND_COLOR,
|
|
||||||
BUS_COLOR,
|
|
||||||
STATION_OUTLINE_WIDTH,
|
|
||||||
STATION_RADIUS,
|
|
||||||
UP_SCALE,
|
|
||||||
} from "./Constants";
|
|
||||||
import "../../styles/MapLayer.css";
|
import "../../styles/MapLayer.css";
|
||||||
import { useGeolocationStore, useCameraAnimationStore } from "../../stores";
|
import { useCameraAnimationStore } from "../../stores";
|
||||||
import { coordinatesToLocal } from "./utils";
|
import { coordinatesToLocal } from "./utils";
|
||||||
import { TramIcon } from "./TramIcon";
|
|
||||||
import { SCALE_FACTOR } from "../../assets/Constants";
|
import { SCALE_FACTOR } from "../../assets/Constants";
|
||||||
import { apiStore } from "../../api/ApiStore/store";
|
import { apiStore } from "../../api/ApiStore/store";
|
||||||
import WebGLMap from "./WebGLMap";
|
import WebGLMap from "./WebGLMap";
|
||||||
@@ -42,7 +31,7 @@ export function Map() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function rotatePoint(x, y, originX, originY, angle) {
|
function rotatePoint(x: number, y: number, originX: number, originY: number, angle: number) {
|
||||||
const cos = Math.cos(angle);
|
const cos = Math.cos(angle);
|
||||||
const sin = Math.sin(angle);
|
const sin = Math.sin(angle);
|
||||||
const dx = x - originX;
|
const dx = x - originX;
|
||||||
@@ -53,8 +42,6 @@ function rotatePoint(x, y, originX, originY, angle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RouteMap = observer(() => {
|
const RouteMap = observer(() => {
|
||||||
const store = useGeolocationStore();
|
|
||||||
const { contextData } = store;
|
|
||||||
const {
|
const {
|
||||||
routeData,
|
routeData,
|
||||||
stationData,
|
stationData,
|
||||||
@@ -77,7 +64,6 @@ const RouteMap = observer(() => {
|
|||||||
scale,
|
scale,
|
||||||
} = useTransform();
|
} = useTransform();
|
||||||
const cameraAnimationStore = useCameraAnimationStore();
|
const cameraAnimationStore = useCameraAnimationStore();
|
||||||
const parentRef = useRef(null);
|
|
||||||
|
|
||||||
const [rotationAngle, setRotationAngle] = useState(0);
|
const [rotationAngle, setRotationAngle] = useState(0);
|
||||||
|
|
||||||
@@ -143,7 +129,7 @@ const RouteMap = observer(() => {
|
|||||||
const rotationOriginY = 0;
|
const rotationOriginY = 0;
|
||||||
|
|
||||||
const transformGeoToMapLocal = useCallback(
|
const transformGeoToMapLocal = useCallback(
|
||||||
(latitude, longitude) => {
|
(latitude: number, longitude: number) => {
|
||||||
if (centerLat === undefined || centerLon === undefined) {
|
if (centerLat === undefined || centerLon === undefined) {
|
||||||
return { x: 0, y: 0 };
|
return { x: 0, y: 0 };
|
||||||
}
|
}
|
||||||
@@ -239,99 +225,6 @@ const RouteMap = observer(() => {
|
|||||||
transformedStations,
|
transformedStations,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const drawActualBusPos = useCallback(
|
|
||||||
(g: Graphics) => {
|
|
||||||
g.clear();
|
|
||||||
if (transformedCurrentCoordinates) {
|
|
||||||
g.circle(
|
|
||||||
transformedCurrentCoordinates.x,
|
|
||||||
transformedCurrentCoordinates.y,
|
|
||||||
STATION_RADIUS / scale < 10
|
|
||||||
? 10
|
|
||||||
: STATION_RADIUS / scale > 20
|
|
||||||
? 20
|
|
||||||
: STATION_RADIUS / scale
|
|
||||||
);
|
|
||||||
g.fill({ color: BUS_COLOR });
|
|
||||||
g.stroke({ color: BACKGROUND_COLOR, width: STATION_OUTLINE_WIDTH });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[transformedCurrentCoordinates, scale]
|
|
||||||
);
|
|
||||||
|
|
||||||
const scaledPoints = useMemo(() => {
|
|
||||||
if (!routeData?.path) return [];
|
|
||||||
|
|
||||||
return routeData.path.map(([latitude, longitude]) => {
|
|
||||||
const { x, y } = transformGeoToMapLocal(latitude, longitude);
|
|
||||||
return rotatePoint(x, y, rotationOriginX, rotationOriginY, rotationAngle);
|
|
||||||
});
|
|
||||||
}, [
|
|
||||||
routeData?.path,
|
|
||||||
transformGeoToMapLocal,
|
|
||||||
rotationOriginX,
|
|
||||||
rotationOriginY,
|
|
||||||
rotationAngle,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const transformedStationsEn = useMemo(() => {
|
|
||||||
if (!stationDataEn) return [];
|
|
||||||
|
|
||||||
return stationDataEn.map((station) => {
|
|
||||||
const { x, y } = transformGeoToMapLocal(
|
|
||||||
station.latitude,
|
|
||||||
station.longitude
|
|
||||||
);
|
|
||||||
const rotatedCoords = rotatePoint(
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
rotationOriginX,
|
|
||||||
rotationOriginY,
|
|
||||||
rotationAngle
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
...station,
|
|
||||||
longitude: rotatedCoords.x,
|
|
||||||
latitude: rotatedCoords.y,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [
|
|
||||||
stationDataEn,
|
|
||||||
transformGeoToMapLocal,
|
|
||||||
rotationOriginX,
|
|
||||||
rotationOriginY,
|
|
||||||
rotationAngle,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const transformedStationsZh = useMemo(() => {
|
|
||||||
if (!stationDataZh) return [];
|
|
||||||
|
|
||||||
return stationDataZh.map((station) => {
|
|
||||||
const { x, y } = transformGeoToMapLocal(
|
|
||||||
station.latitude,
|
|
||||||
station.longitude
|
|
||||||
);
|
|
||||||
const rotatedCoords = rotatePoint(
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
rotationOriginX,
|
|
||||||
rotationOriginY,
|
|
||||||
rotationAngle
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
...station,
|
|
||||||
longitude: rotatedCoords.x,
|
|
||||||
latitude: rotatedCoords.y,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [
|
|
||||||
stationDataZh,
|
|
||||||
transformGeoToMapLocal,
|
|
||||||
rotationOriginX,
|
|
||||||
rotationOriginY,
|
|
||||||
rotationAngle,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!routeData ||
|
!routeData ||
|
||||||
!stationData ||
|
!stationData ||
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// SightsLayer.tsx
|
// SightsLayer.tsx
|
||||||
import React from "react";
|
|
||||||
import { Graphics, Assets, Texture, TextStyle } from "pixi.js";
|
import { Graphics, Assets, Texture, TextStyle } from "pixi.js";
|
||||||
import { useCallback, useEffect, useState, useMemo } from "react";
|
import { useCallback, useEffect, useState, useMemo } from "react";
|
||||||
import { useTransform } from "./transformContext";
|
import { useTransform } from "./transformContext";
|
||||||
@@ -9,7 +8,6 @@ import { useGeolocationStore } from "../../stores"; // Импортируем us
|
|||||||
|
|
||||||
const BASE_ICON_SIZE = 30;
|
const BASE_ICON_SIZE = 30;
|
||||||
const CLUSTER_RADIUS_BASE = 10;
|
const CLUSTER_RADIUS_BASE = 10;
|
||||||
const CLUSTER_COLOR = 0x1a73e8;
|
|
||||||
|
|
||||||
type Cluster = {
|
type Cluster = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -150,7 +148,7 @@ function SingleSight({
|
|||||||
readonly sight: SightData;
|
readonly sight: SightData;
|
||||||
onSightClick: (sightId: string) => void;
|
onSightClick: (sightId: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const { scale } = useTransform();
|
useTransform();
|
||||||
const [texture, setTexture] = useState<Texture>(Texture.EMPTY);
|
const [texture, setTexture] = useState<Texture>(Texture.EMPTY);
|
||||||
const store = useGeolocationStore();
|
const store = useGeolocationStore();
|
||||||
const { setIsGovernorWidgetOpen } = store;
|
const { setIsGovernorWidgetOpen } = store;
|
||||||
@@ -197,7 +195,7 @@ function SightCluster({
|
|||||||
}) {
|
}) {
|
||||||
const store = useGeolocationStore();
|
const store = useGeolocationStore();
|
||||||
const { setIsGovernorWidgetOpen } = store;
|
const { setIsGovernorWidgetOpen } = store;
|
||||||
const { scale } = useTransform();
|
useTransform();
|
||||||
const radius = CLUSTER_RADIUS_BASE;
|
const radius = CLUSTER_RADIUS_BASE;
|
||||||
const [texture, setTexture] = useState<Texture>(Texture.EMPTY);
|
const [texture, setTexture] = useState<Texture>(Texture.EMPTY);
|
||||||
const fontSize = 14;
|
const fontSize = 14;
|
||||||
@@ -334,7 +332,7 @@ export function SightsLayer({
|
|||||||
sights,
|
sights,
|
||||||
pathPoints,
|
pathPoints,
|
||||||
}: Readonly<SightsLayerProps>) {
|
}: Readonly<SightsLayerProps>) {
|
||||||
const { scale } = useTransform();
|
useTransform();
|
||||||
const distanceThreshold = BASE_ICON_SIZE * 3;
|
const distanceThreshold = BASE_ICON_SIZE * 3;
|
||||||
|
|
||||||
const store = useGeolocationStore(); // Получаем доступ к MobX хранилищу
|
const store = useGeolocationStore(); // Получаем доступ к MobX хранилищу
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import { Texture, Assets } from "pixi.js";
|
||||||
import { Texture, Assets, Graphics } from "pixi.js";
|
|
||||||
import { useEffect, useState, useMemo, useRef } from "react";
|
import { useEffect, useState, useMemo, useRef } from "react";
|
||||||
import { useTransform } from "./transformContext";
|
import { useTransform } from "./transformContext";
|
||||||
import { lerp, lerpAngle } from "../../utils/animationUtils";
|
import { lerp, lerpAngle } from "../../utils/animationUtils";
|
||||||
@@ -11,8 +10,6 @@ const basePath = new URL(
|
|||||||
const tramPath = new URL("../../assets/tramPosition/Tram.svg", import.meta.url)
|
const tramPath = new URL("../../assets/tramPosition/Tram.svg", import.meta.url)
|
||||||
.href;
|
.href;
|
||||||
|
|
||||||
// Константы анимации (как в HTML файле)
|
|
||||||
const ANIMATION_DURATION = 1200; // 1.2 секунды
|
|
||||||
const LERP_SPEED = 0.1; // Скорость интерполяции (10% каждый кадр)
|
const LERP_SPEED = 0.1; // Скорость интерполяции (10% каждый кадр)
|
||||||
|
|
||||||
// Функция для проверки расстояния до ближайшей точки маршрута
|
// Функция для проверки расстояния до ближайшей точки маршрута
|
||||||
@@ -101,7 +98,7 @@ const getDistanceToStations = (
|
|||||||
offset_x?: number;
|
offset_x?: number;
|
||||||
offset_y?: number;
|
offset_y?: number;
|
||||||
}[],
|
}[],
|
||||||
debug: boolean = false
|
_debug: boolean = false
|
||||||
) => {
|
) => {
|
||||||
if (!stations || stations.length === 0) {
|
if (!stations || stations.length === 0) {
|
||||||
return Infinity;
|
return Infinity;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState, useRef } from "react";
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
import { lerp, lerpAngle } from "../../utils/animationUtils";
|
import { lerpAngle } from "../../utils/animationUtils";
|
||||||
import tramBase from "../../assets/tramPosition/Tram Base.svg";
|
import tramBase from "../../assets/tramPosition/Tram Base.svg";
|
||||||
import tramSvg from "../../assets/tramPosition/Tram_Second.svg";
|
import tramSvg from "../../assets/tramPosition/Tram_Second.svg";
|
||||||
import { getMediaUrl } from "../../api/apiConfig";
|
import { getMediaUrl } from "../../api/apiConfig";
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import {
|
|||||||
BUS_COLOR,
|
BUS_COLOR,
|
||||||
BASE_ICON_SIZE,
|
BASE_ICON_SIZE,
|
||||||
CLUSTER_RADIUS_BASE,
|
CLUSTER_RADIUS_BASE,
|
||||||
CLUSTER_COLOR,
|
|
||||||
ACTIVE_STATION_COLOR,
|
|
||||||
} from "./Constants";
|
} from "./Constants";
|
||||||
import { SCALE_FACTOR } from "../../assets/Constants";
|
import { SCALE_FACTOR } from "../../assets/Constants";
|
||||||
import { apiStore } from "../../api/ApiStore/store";
|
import { apiStore } from "../../api/ApiStore/store";
|
||||||
@@ -40,7 +38,7 @@ const YELLOW_ICON_FILTER =
|
|||||||
const clamp = (value: number, min: number, max: number) =>
|
const clamp = (value: number, min: number, max: number) =>
|
||||||
Math.min(max, Math.max(min, value));
|
Math.min(max, Math.max(min, value));
|
||||||
|
|
||||||
const debugWebglLog = (...args: unknown[]) => {
|
const debugWebglLog = (..._args: unknown[]) => {
|
||||||
if (!DEBUG_WEBGL_ROUTE_MAP) return;
|
if (!DEBUG_WEBGL_ROUTE_MAP) return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -424,7 +422,7 @@ export const WebGLMap = observer(() => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const clampPosition = useCallback(
|
const clampPosition = useCallback(
|
||||||
(pos: { x: number; y: number }, currentScale: number) => {
|
(pos: { x: number; y: number }, _currentScale: number) => {
|
||||||
return pos;
|
return pos;
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
@@ -1165,7 +1163,6 @@ export const WebGLMap = observer(() => {
|
|||||||
gl.enableVertexAttribArray(attribs.a_pos);
|
gl.enableVertexAttribArray(attribs.a_pos);
|
||||||
gl.vertexAttribPointer(attribs.a_pos, 2, gl.FLOAT, false, 0, 0);
|
gl.vertexAttribPointer(attribs.a_pos, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
|
||||||
const vcount = routePath.length / 2;
|
|
||||||
let tramSegIndex = getCurrentSegIndex();
|
let tramSegIndex = getCurrentSegIndex();
|
||||||
|
|
||||||
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||||
@@ -1452,53 +1449,6 @@ export const WebGLMap = observer(() => {
|
|||||||
|
|
||||||
const toPointsArray = (arr: number[]) => new Float32Array(arr);
|
const toPointsArray = (arr: number[]) => new Float32Array(arr);
|
||||||
|
|
||||||
const pathPts: { x: number; y: number }[] = [];
|
|
||||||
for (let i = 0; i < routePath.length; i += 2)
|
|
||||||
pathPts.push({ x: routePath[i], y: routePath[i + 1] });
|
|
||||||
const getSeg = (px: number, py: number) => {
|
|
||||||
if (pathPts.length < 2) return -1;
|
|
||||||
let best = -1,
|
|
||||||
bestD = Infinity;
|
|
||||||
for (let i = 0; i < pathPts.length - 1; i++) {
|
|
||||||
const p1 = pathPts[i],
|
|
||||||
p2 = pathPts[i + 1];
|
|
||||||
const dx = p2.x - p1.x,
|
|
||||||
dy = p2.y - p1.y;
|
|
||||||
const len2 = dx * dx + dy * dy;
|
|
||||||
if (!len2) continue;
|
|
||||||
const t = ((px - p1.x) * dx + (py - p1.y) * dy) / len2;
|
|
||||||
const tt = Math.max(0, Math.min(1, t));
|
|
||||||
const cx = p1.x + tt * dx,
|
|
||||||
cy = p1.y + tt * dy;
|
|
||||||
const d = Math.hypot(px - cx, py - cy);
|
|
||||||
if (d < bestD) {
|
|
||||||
bestD = d;
|
|
||||||
best = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best;
|
|
||||||
};
|
|
||||||
|
|
||||||
let tramSegForStations = -1;
|
|
||||||
{
|
|
||||||
const cLat = routeData?.center_latitude,
|
|
||||||
cLon = routeData?.center_longitude;
|
|
||||||
const tram = apiStore?.context?.currentCoordinates as any;
|
|
||||||
if (tram && cLat !== undefined && cLon !== undefined) {
|
|
||||||
const loc = coordinatesToLocal(
|
|
||||||
tram.latitude - cLat,
|
|
||||||
tram.longitude - cLon,
|
|
||||||
);
|
|
||||||
const wx = loc.x * UP_SCALE,
|
|
||||||
wy = loc.y * UP_SCALE;
|
|
||||||
const cosR = Math.cos(rotationAngle),
|
|
||||||
sinR = Math.sin(rotationAngle);
|
|
||||||
const tx = wx * cosR - wy * sinR,
|
|
||||||
ty = wx * sinR + wy * cosR;
|
|
||||||
tramSegForStations = getSeg(tx, ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let activeStationIndex = -1;
|
let activeStationIndex = -1;
|
||||||
const tramCoords = apiStore?.context?.currentCoordinates;
|
const tramCoords = apiStore?.context?.currentCoordinates;
|
||||||
if (
|
if (
|
||||||
@@ -1719,10 +1669,10 @@ export const WebGLMap = observer(() => {
|
|||||||
const sin = Math.sin(rotationAngle);
|
const sin = Math.sin(rotationAngle);
|
||||||
|
|
||||||
const startStationData = stationData.find(
|
const startStationData = stationData.find(
|
||||||
(station) => station.id.toString() === apiStore.context?.startStopId,
|
(station: any) => station.id.toString() === apiStore.context?.startStopId,
|
||||||
);
|
);
|
||||||
const endStationData = stationData.find(
|
const endStationData = stationData.find(
|
||||||
(station) => station.id.toString() === apiStore.context?.endStopId,
|
(station: any) => station.id.toString() === apiStore.context?.endStopId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const terminalStations: number[] = [];
|
const terminalStations: number[] = [];
|
||||||
@@ -2331,11 +2281,10 @@ export const WebGLMap = observer(() => {
|
|||||||
? { right: 0, transform: "none" }
|
? { right: 0, transform: "none" }
|
||||||
: { left: "50%", transform: "translateX(-50%)" };
|
: { left: "50%", transform: "translateX(-50%)" };
|
||||||
|
|
||||||
const apiBaseUrl = apiBaseURL;
|
|
||||||
const isMediaIdEmptyResult = isMediaIdEmpty(station?.icon);
|
const isMediaIdEmptyResult = isMediaIdEmpty(station?.icon);
|
||||||
const iconSrc = isMediaIdEmptyResult
|
const iconSrc = isMediaIdEmptyResult
|
||||||
? null
|
? null
|
||||||
: `${apiBaseUrl}/media/${station?.icon}/download`;
|
: buildMediaDownloadUrl(mediaBaseUrl, station!.icon!, mediaToken);
|
||||||
const iconSizePx = Math.round(primaryFontSize * 1.2);
|
const iconSizePx = Math.round(primaryFontSize * 1.2);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import React, {
|
|||||||
useContext,
|
useContext,
|
||||||
useState,
|
useState,
|
||||||
useCallback,
|
useCallback,
|
||||||
useRef,
|
|
||||||
} from "react";
|
} from "react";
|
||||||
import { UP_SCALE } from "./Constants";
|
|
||||||
|
|
||||||
const TransformContext = createContext<{
|
const TransformContext = createContext<{
|
||||||
position: { x: number; y: number };
|
position: { x: number; y: number };
|
||||||
|
|||||||
@@ -231,7 +231,6 @@ export const ThreeView: React.FC<ThreeViewProps> = ({
|
|||||||
height = "100%",
|
height = "100%",
|
||||||
onLoad,
|
onLoad,
|
||||||
onError,
|
onError,
|
||||||
onAspectRatioCalculated,
|
|
||||||
controlRef,
|
controlRef,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
@@ -244,7 +243,7 @@ export const ThreeView: React.FC<ThreeViewProps> = ({
|
|||||||
}}
|
}}
|
||||||
camera={{ position: [0, 0, 5], fov: 40 }}
|
camera={{ position: [0, 0, 5], fov: 40 }}
|
||||||
style={{ width: "100%", height: "100%" }}
|
style={{ width: "100%", height: "100%" }}
|
||||||
onError={(e) => onError?.(e.message)}
|
onError={(e: any) => onError?.(e.message)}
|
||||||
>
|
>
|
||||||
<AutoResize />
|
<AutoResize />
|
||||||
<TouchController />
|
<TouchController />
|
||||||
@@ -269,7 +268,7 @@ export const ThreeView: React.FC<ThreeViewProps> = ({
|
|||||||
<Stage
|
<Stage
|
||||||
environment={null}
|
environment={null}
|
||||||
intensity={1}
|
intensity={1}
|
||||||
contactShadow={false}
|
castShadow={false}
|
||||||
shadows={false}
|
shadows={false}
|
||||||
adjustCamera={true}
|
adjustCamera={true}
|
||||||
center={{ precise: true }}
|
center={{ precise: true }}
|
||||||
|
|||||||
@@ -52,33 +52,6 @@ class CameraAnimationStore {
|
|||||||
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateDistance(p1: CameraPosition, p2: CameraPosition): number {
|
|
||||||
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
private isNearStation(
|
|
||||||
tramPos: CameraPosition,
|
|
||||||
stations: Station[]
|
|
||||||
): { isNear: boolean; distance: number } {
|
|
||||||
if (!stations || stations.length === 0)
|
|
||||||
return { isNear: false, distance: Infinity };
|
|
||||||
const threshold = 300; // Порог в координатах карты
|
|
||||||
let minDistance = Infinity;
|
|
||||||
|
|
||||||
for (const station of stations) {
|
|
||||||
const distance = this.calculateDistance(tramPos, {
|
|
||||||
x: station.longitude,
|
|
||||||
y: station.latitude,
|
|
||||||
});
|
|
||||||
minDistance = Math.min(minDistance, distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isNear: minDistance < threshold,
|
|
||||||
distance: minDistance,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public setUpdateCallback(
|
public setUpdateCallback(
|
||||||
callback: ((pos: CameraPosition, zoom: number) => void) | null
|
callback: ((pos: CameraPosition, zoom: number) => void) | null
|
||||||
) {
|
) {
|
||||||
@@ -140,7 +113,7 @@ class CameraAnimationStore {
|
|||||||
public followTram(
|
public followTram(
|
||||||
tramMapPos: CameraPosition,
|
tramMapPos: CameraPosition,
|
||||||
screenCenter: CameraPosition,
|
screenCenter: CameraPosition,
|
||||||
stations: Station[] = []
|
_stations: Station[] = []
|
||||||
) {
|
) {
|
||||||
// Анимация начинается с текущего зума и плавно переходит к максимальному зуму
|
// Анимация начинается с текущего зума и плавно переходит к максимальному зуму
|
||||||
// для плавного приближения к желтой точке при слежении
|
// для плавного приближения к желтой точке при слежении
|
||||||
|
|||||||
@@ -215,6 +215,8 @@
|
|||||||
transition:
|
transition:
|
||||||
transform 0.3s ease-out,
|
transform 0.3s ease-out,
|
||||||
opacity 0.3s ease-out;
|
opacity 0.3s ease-out;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-sights.slide-in {
|
.side-menu-sights.slide-in {
|
||||||
@@ -227,7 +229,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-sights-block {
|
.side-menu-sights-block {
|
||||||
height: calc(100% - 20px);
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
@@ -236,6 +239,7 @@
|
|||||||
max-width: calc(100% - 20px);
|
max-width: calc(100% - 20px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-sight {
|
.side-menu-sight {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { apiStore } from "../../../client/src/api/ApiStore/store";
|
import { apiStore } from "../../../client/src/api/ApiStore/store";
|
||||||
import App from "../../../client/src/App";
|
import App from "../../../client/src/App";
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
SelectArticleModal,
|
SelectArticleModal,
|
||||||
UploadMediaDialog,
|
UploadMediaDialog,
|
||||||
Language,
|
Language,
|
||||||
articlesStore,
|
|
||||||
} from "@shared";
|
} from "@shared";
|
||||||
import {
|
import {
|
||||||
LanguageSwitcher,
|
LanguageSwitcher,
|
||||||
@@ -85,31 +84,7 @@ export const LeftWidgetTab = observer(
|
|||||||
) => {
|
) => {
|
||||||
setIsSelectArticleDialogOpen(false);
|
setIsSelectArticleDialogOpen(false);
|
||||||
|
|
||||||
const ruArticle = await articlesStore.getArticle(articleId, "ru");
|
await editSightStore.getLeftArticle(articleId);
|
||||||
const enArticle = await articlesStore.getArticle(articleId, "en");
|
|
||||||
const zhArticle = await articlesStore.getArticle(articleId, "zh");
|
|
||||||
|
|
||||||
updateSightInfo("ru", {
|
|
||||||
left: {
|
|
||||||
heading: ruArticle.data.heading,
|
|
||||||
body: ruArticle.data.body,
|
|
||||||
media: ruArticle.data.media || [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
updateSightInfo("en", {
|
|
||||||
left: {
|
|
||||||
heading: enArticle.data.heading,
|
|
||||||
body: enArticle.data.body,
|
|
||||||
media: enArticle.data.media || [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
updateSightInfo("zh", {
|
|
||||||
left: {
|
|
||||||
heading: zhArticle.data.heading,
|
|
||||||
body: zhArticle.data.body,
|
|
||||||
media: zhArticle.data.media || [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
updateSightInfo(
|
updateSightInfo(
|
||||||
languageStore.language,
|
languageStore.language,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user