// function initWebGLContext( // canvas: HTMLCanvasElement // ): WebGLRenderingContext | null { // const gl = // (canvas.getContext("webgl") as WebGLRenderingContext | null) || // (canvas.getContext("experimental-webgl") as WebGLRenderingContext | null); // return gl; // } // function resizeCanvasToDisplaySize(canvas: HTMLCanvasElement): boolean { // const dpr = Math.max(1, window.devicePixelRatio || 1); // const displayWidth = Math.floor(canvas.clientWidth * dpr); // const displayHeight = Math.floor(canvas.clientHeight * dpr); // if (canvas.width !== displayWidth || canvas.height !== displayHeight) { // canvas.width = displayWidth; // canvas.height = displayHeight; // return true; // } // return false; // } // export const WebGLMap = observer(() => { // const canvasRef = useRef(null); // const glRef = useRef(null); // const programRef = useRef(null); // const bufferRef = useRef(null); // const pointProgramRef = useRef(null); // const pointBufferRef = useRef(null); // const screenLineProgramRef = useRef(null); // const screenLineBufferRef = useRef(null); // const attribsRef = useRef<{ a_pos: number } | null>(null); // const uniformsRef = useRef<{ // u_cameraPos: WebGLUniformLocation | null; // u_scale: WebGLUniformLocation | null; // u_resolution: WebGLUniformLocation | null; // u_color: WebGLUniformLocation | null; // } | null>(null); // const { routeData, stationData, stationDataEn, stationDataZh, sightData } = // useMapData() as any; // const { // position, // scale, // setPosition, // setScale, // isAutoMode, // setIsAutoMode, // screenCenter, // setScreenCenter, // userActivityTimestamp, // updateUserActivity, // } = useTransform(); // const cameraAnimationStore = useCameraAnimationStore(); // const scaleLimitsRef = useRef({ // min: null as number | null, // max: null as number | null, // }); // useEffect(() => { // if ( // routeData?.scale_min !== undefined && // routeData?.scale_max !== undefined // ) { // scaleLimitsRef.current = { // min: routeData.scale_min / 10, // max: routeData.scale_max / 10, // }; // } // }, [routeData?.scale_min, routeData?.scale_max]); // const clampScale = useCallback((value: number) => { // const { min, max } = scaleLimitsRef.current; // if (min === null || max === null) { // return value; // } // const clampedValue = Math.max(min, Math.min(max, value)); // return clampedValue; // }, []); // const { selectedLanguage } = useGeolocationStore(); // const positionRef = useRef(position); // const scaleRef = useRef(scale); // const setPositionRef = useRef(setPosition); // const setScaleRef = useRef(setScale); // useEffect(() => { // setPositionRef.current = setPosition; // }, [setPosition]); // useEffect(() => { // setScaleRef.current = setScale; // }, [setScale]); // useEffect(() => { // if (routeData) { // } // }, [routeData]); // useEffect(() => { // positionRef.current = position; // }, [position]); // useEffect(() => { // scaleRef.current = scale; // }, [scale]); // const rotationAngle = useMemo(() => { // const deg = (routeData as any)?.rotate ?? 0; // return (deg * Math.PI) / 180; // }, [routeData]); // const { // position: animatedYellowDotPosition, // animateTo: animateYellowDotTo, // setPositionImmediate: setYellowDotPositionImmediate, // } = useAnimatedPolarPosition(0, 0, 800); // const routePath = useMemo(() => { // if (!routeData?.path || routeData?.path.length === 0) // return new Float32Array(); // const centerLat = routeData.center_latitude; // const centerLon = routeData.center_longitude; // if (centerLat === undefined || centerLon === undefined) // return new Float32Array(); // const cos = Math.cos(rotationAngle); // const sin = Math.sin(rotationAngle); // const verts: number[] = []; // for (const [lat, lon] of routeData.path) { // const local = coordinatesToLocal(lat - centerLat, lon - centerLon); // const x = local.x * UP_SCALE; // const y = local.y * UP_SCALE; // const rx = x * cos - y * sin; // const ry = x * sin + y * cos; // verts.push(rx, ry); // } // return new Float32Array(verts); // }, [ // routeData?.path, // routeData?.center_latitude, // routeData?.center_longitude, // rotationAngle, // ]); // const transformedTramCoords = useMemo(() => { // const centerLat = routeData?.center_latitude; // const centerLon = routeData?.center_longitude; // if (centerLat === undefined || centerLon === undefined) return null; // const coords: any = apiStore?.context?.currentCoordinates; // if (!coords) return null; // const local = coordinatesToLocal( // coords.latitude - centerLat, // coords.longitude - centerLon // ); // const x = local.x * UP_SCALE; // const y = local.y * UP_SCALE; // const cos = Math.cos(rotationAngle); // const sin = Math.sin(rotationAngle); // const rx = x * cos - y * sin; // const ry = x * sin + y * cos; // return { x: rx, y: ry }; // }, [ // routeData?.center_latitude, // routeData?.center_longitude, // apiStore?.context?.currentCoordinates, // rotationAngle, // ]); // useEffect(() => { // const callback = (newPos: { x: number; y: number }, newZoom: number) => { // setPosition(newPos); // setScale(newZoom); // }; // cameraAnimationStore.setUpdateCallback(callback); // cameraAnimationStore.syncState(position, scale); // return () => { // cameraAnimationStore.setUpdateCallback(null); // }; // }, []); // useEffect(() => { // if ( // routeData?.scale_min !== undefined && // routeData?.scale_max !== undefined // ) { // cameraAnimationStore.setMaxZoom(routeData.scale_max / SCALE_FACTOR); // cameraAnimationStore.setMinZoom(routeData.scale_min / SCALE_FACTOR); // } // }, [routeData?.scale_min, routeData?.scale_max, cameraAnimationStore]); // useEffect(() => { // const interval = setInterval(() => { // const timeSinceActivity = Date.now() - userActivityTimestamp; // if (timeSinceActivity >= 5000 && !isAutoMode) { // setIsAutoMode(true); // } // }, 1000); // return () => clearInterval(interval); // }, [userActivityTimestamp, isAutoMode, setIsAutoMode]); // useEffect(() => { // if (cameraAnimationStore.isActivelyAnimating) { // return; // } // if (isAutoMode && transformedTramCoords && screenCenter) { // const transformedStations = stationData // ? stationData // .map((station: any) => { // const centerLat = routeData?.center_latitude; // const centerLon = routeData?.center_longitude; // if (centerLat === undefined || centerLon === undefined) // return null; // const local = coordinatesToLocal( // station.latitude - centerLat, // station.longitude - centerLon // ); // const x = local.x * UP_SCALE; // const y = local.y * UP_SCALE; // const cos = Math.cos(rotationAngle); // const sin = Math.sin(rotationAngle); // const rx = x * cos - y * sin; // const ry = x * sin + y * cos; // return { // longitude: rx, // latitude: ry, // id: station.id, // }; // }) // .filter(Boolean) // : []; // if ( // transformedTramCoords && // screenCenter && // transformedStations && // scaleLimitsRef.current !== null && // scaleLimitsRef.current.max !== null && // scaleLimitsRef.current.min && // scaleLimitsRef.current.min !== null // ) { // cameraAnimationStore.setMaxZoom(scaleLimitsRef.current!.max); // cameraAnimationStore.setMinZoom(scaleLimitsRef.current!.min); // cameraAnimationStore.syncState(positionRef.current, scaleRef.current); // cameraAnimationStore.followTram( // transformedTramCoords, // screenCenter, // transformedStations // ); // } // } else if (!isAutoMode) { // cameraAnimationStore.stopAnimation(); // } // }, [ // isAutoMode, // transformedTramCoords, // screenCenter, // cameraAnimationStore, // stationData, // routeData, // rotationAngle, // ]); // const stationLabels = useMemo(() => { // if (!stationData || !routeData) // return [] as Array<{ x: number; y: number; name: string; sub?: string }>; // const centerLat = routeData.center_latitude; // const centerLon = routeData.center_longitude; // if (centerLat === undefined || centerLon === undefined) return []; // const cos = Math.cos(rotationAngle); // const sin = Math.sin(rotationAngle); // const result: Array<{ x: number; y: number; name: string; sub?: string }> = // []; // for (let i = 0; i < stationData.length; i++) { // const st = stationData[i]; // const local = coordinatesToLocal( // st.latitude - centerLat, // st.longitude - centerLon // ); // const x = local.x * UP_SCALE; // const y = local.y * UP_SCALE; // const rx = x * cos - y * sin; // const ry = x * sin + y * cos; // const DEFAULT_LABEL_OFFSET_X = 25; // const DEFAULT_LABEL_OFFSET_Y = 0; // const labelOffsetX = // st.offset_x === 0 && st.offset_y === 0 // ? DEFAULT_LABEL_OFFSET_X // : st.offset_x; // const labelOffsetY = // st.offset_x === 0 && st.offset_y === 0 // ? DEFAULT_LABEL_OFFSET_Y // : st.offset_y; // const textBlockPositionX = rx + labelOffsetX; // const textBlockPositionY = ry + labelOffsetY; // const dpr = Math.max( // 1, // (typeof window !== "undefined" && window.devicePixelRatio) || 1 // ); // const sx = (textBlockPositionX * scale + position.x) / dpr; // const sy = (textBlockPositionY * scale + position.y) / dpr; // let sub: string | undefined; // if ((selectedLanguage as any) === "zh") // sub = (stationDataZh as any)?.[i]?.name; // else if ( // (selectedLanguage as any) === "en" || // (selectedLanguage as any) === "ru" // ) // sub = (stationDataEn as any)?.[i]?.name; // result.push({ x: sx, y: sy, name: st.name, sub }); // } // return result; // }, [ // stationData, // stationDataEn as any, // stationDataZh as any, // position.x, // position.y, // scale, // routeData?.center_latitude, // routeData?.center_longitude, // rotationAngle, // selectedLanguage as any, // ]); // const stationPoints = useMemo(() => { // if (!stationData || !routeData) return new Float32Array(); // const centerLat = routeData.center_latitude; // const centerLon = routeData.center_longitude; // if (centerLat === undefined || centerLon === undefined) // return new Float32Array(); // const cos = Math.cos(rotationAngle); // const sin = Math.sin(rotationAngle); // const verts: number[] = []; // for (const s of stationData) { // const local = coordinatesToLocal( // s.latitude - centerLat, // s.longitude - centerLon // ); // const x = local.x * UP_SCALE; // const y = local.y * UP_SCALE; // const rx = x * cos - y * sin; // const ry = x * sin + y * cos; // verts.push(rx, ry); // } // return new Float32Array(verts); // }, [ // stationData, // routeData?.center_latitude, // routeData?.center_longitude, // rotationAngle, // ]); // const sightPoints = useMemo(() => { // if (!sightData || !routeData) return new Float32Array(); // const centerLat = routeData.center_latitude; // const centerLon = routeData.center_longitude; // if (centerLat === undefined || centerLon === undefined) // return new Float32Array(); // const cos = Math.cos(rotationAngle); // const sin = Math.sin(rotationAngle); // const verts: number[] = []; // for (const s of sightData) { // const local = coordinatesToLocal( // s.latitude - centerLat, // s.longitude - centerLon // ); // const x = local.x * UP_SCALE; // const y = local.y * UP_SCALE; // const rx = x * cos - y * sin; // const ry = x * sin + y * cos; // verts.push(rx, ry); // } // return new Float32Array(verts); // }, [ // sightData, // routeData?.center_latitude, // routeData?.center_longitude, // rotationAngle, // ]); // useEffect(() => { // const canvas = canvasRef.current; // if (!canvas) return; // const gl = initWebGLContext(canvas); // glRef.current = gl; // if (!gl) return; // const vertSrc = ` // attribute vec2 a_pos; // uniform vec2 u_cameraPos; // uniform float u_scale; // uniform vec2 u_resolution; // void main() { // vec2 screen = a_pos * u_scale + u_cameraPos; // vec2 zeroToOne = screen / u_resolution; // vec2 zeroToTwo = zeroToOne * 2.0; // vec2 clip = zeroToTwo - 1.0; // gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0); // } // `; // const fragSrc = ` // precision mediump float; // uniform vec4 u_color; // void main() { // gl_FragColor = u_color; // } // `; // const compile = (type: number, src: string) => { // const s = gl.createShader(type)!; // gl.shaderSource(s, src); // gl.compileShader(s); // return s; // }; // const vs = compile(gl.VERTEX_SHADER, vertSrc); // const fs = compile(gl.FRAGMENT_SHADER, fragSrc); // const prog = gl.createProgram()!; // gl.attachShader(prog, vs); // gl.attachShader(prog, fs); // gl.linkProgram(prog); // programRef.current = prog; // gl.useProgram(prog); // const a_pos = gl.getAttribLocation(prog, "a_pos"); // const u_cameraPos = gl.getUniformLocation(prog, "u_cameraPos"); // const u_scale = gl.getUniformLocation(prog, "u_scale"); // const u_resolution = gl.getUniformLocation(prog, "u_resolution"); // const u_color = gl.getUniformLocation(prog, "u_color"); // attribsRef.current = { a_pos }; // uniformsRef.current = { u_cameraPos, u_scale, u_resolution, u_color }; // const buffer = gl.createBuffer(); // bufferRef.current = buffer; // const pointVert = ` // attribute vec2 a_pos; // uniform vec2 u_cameraPos; // uniform float u_scale; // uniform vec2 u_resolution; // uniform float u_pointSize; // void main() { // vec2 screen = a_pos * u_scale + u_cameraPos; // vec2 zeroToOne = screen / u_resolution; // vec2 zeroToTwo = zeroToOne * 2.0; // vec2 clip = zeroToTwo - 1.0; // gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0); // gl_PointSize = u_pointSize; // } // `; // const pointFrag = ` // precision mediump float; // uniform vec4 u_color; // void main() { // vec2 c = gl_PointCoord * 2.0 - 1.0; // float d = dot(c, c); // if (d > 1.0) discard; // gl_FragColor = u_color; // } // `; // const vs2 = compile(gl.VERTEX_SHADER, pointVert); // const fs2 = compile(gl.FRAGMENT_SHADER, pointFrag); // const pprog = gl.createProgram()!; // gl.attachShader(pprog, vs2); // gl.attachShader(pprog, fs2); // gl.linkProgram(pprog); // pointProgramRef.current = pprog; // pointBufferRef.current = gl.createBuffer(); // const lineVert = ` // attribute vec2 a_screen; // uniform vec2 u_resolution; // void main(){ // vec2 zeroToOne = a_screen / u_resolution; // vec2 clip = zeroToOne * 2.0 - 1.0; // gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0); // } // `; // const lineFrag = ` // precision mediump float; // uniform vec4 u_color; // void main(){ gl_FragColor = u_color; } // `; // const lv = compile(gl.VERTEX_SHADER, lineVert); // const lf = compile(gl.FRAGMENT_SHADER, lineFrag); // const lprog = gl.createProgram()!; // gl.attachShader(lprog, lv); // gl.attachShader(lprog, lf); // gl.linkProgram(lprog); // screenLineProgramRef.current = lprog; // screenLineBufferRef.current = gl.createBuffer(); // const handleResize = () => { // const changed = resizeCanvasToDisplaySize(canvas); // if (!gl) return; // setScreenCenter({ // x: canvas.width / 2, // y: canvas.height / 2, // }); // if (changed) { // gl.viewport(0, 0, canvas.width, canvas.height); // } // gl.clearColor(0, 0, 0, 1); // gl.clear(gl.COLOR_BUFFER_BIT); // }; // handleResize(); // window.addEventListener("resize", handleResize); // return () => { // window.removeEventListener("resize", handleResize); // }; // }, []); // useEffect(() => { // const centerLat = routeData?.center_latitude; // const centerLon = routeData?.center_longitude; // if (centerLat !== undefined && centerLon !== undefined) { // const coords: any = apiStore?.context?.currentCoordinates; // if (coords) { // const local = coordinatesToLocal( // coords.latitude - centerLat, // coords.longitude - centerLon // ); // const x = local.x * UP_SCALE; // const y = local.y * UP_SCALE; // const cos = Math.cos(rotationAngle), // sin = Math.sin(rotationAngle); // const rx = x * cos - y * sin; // const ry = x * sin + y * cos; // if (isAutoMode) { // animateYellowDotTo(rx, ry); // } else { // setYellowDotPositionImmediate(rx, ry); // } // } // } // }, [ // routeData?.center_latitude, // routeData?.center_longitude, // rotationAngle, // apiStore?.context?.currentCoordinates?.latitude, // apiStore?.context?.currentCoordinates?.longitude, // isAutoMode, // animateYellowDotTo, // setYellowDotPositionImmediate, // ]); // useEffect(() => { // const gl = glRef.current; // const canvas = canvasRef.current; // const prog = programRef.current; // const buffer = bufferRef.current; // const attribs = attribsRef.current; // const uniforms = uniformsRef.current; // const pprog = pointProgramRef.current; // const pbuffer = pointBufferRef.current; // if ( // !gl || // !canvas || // !prog || // !buffer || // !attribs || // !uniforms || // !pprog || // !pbuffer // ) // return; // gl.viewport(0, 0, canvas.width, canvas.height); // gl.clearColor(0, 0, 0, 1); // gl.clear(gl.COLOR_BUFFER_BIT); // gl.useProgram(prog); // gl.uniform2f(uniforms.u_cameraPos, position.x, position.y); // gl.uniform1f(uniforms.u_scale, scale); // gl.uniform2f(uniforms.u_resolution, canvas.width, canvas.height); // gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // gl.bufferData(gl.ARRAY_BUFFER, routePath, gl.STATIC_DRAW); // gl.enableVertexAttribArray(attribs.a_pos); // gl.vertexAttribPointer(attribs.a_pos, 2, gl.FLOAT, false, 0, 0); // const vcount = routePath.length / 2; // let tramSegIndex = -1; // { // const centerLatTmp = routeData?.center_latitude; // const centerLonTmp = routeData?.center_longitude; // if (centerLatTmp !== undefined && centerLonTmp !== undefined) { // const coordsAny: any = apiStore?.context?.currentCoordinates; // if (coordsAny) { // const loc = coordinatesToLocal( // coordsAny.latitude - centerLatTmp, // coordsAny.longitude - centerLonTmp // ); // const wx = loc.x * UP_SCALE; // const wy = loc.y * UP_SCALE; // const cosR = Math.cos(rotationAngle), // sinR = Math.sin(rotationAngle); // const tX = wx * cosR - wy * sinR; // const tY = wx * sinR + wy * cosR; // let best = -1, // bestD = Infinity; // for (let i = 0; i < vcount - 1; i++) { // const p1x = routePath[i * 2], // p1y = routePath[i * 2 + 1]; // const p2x = routePath[(i + 1) * 2], // p2y = routePath[(i + 1) * 2 + 1]; // const dx = p2x - p1x, // dy = p2y - p1y; // const len2 = dx * dx + dy * dy; // if (!len2) continue; // const t = ((tX - p1x) * dx + (tY - p1y) * dy) / len2; // const cl = Math.max(0, Math.min(1, t)); // const px = p1x + cl * dx, // py = p1y + cl * dy; // const d = Math.hypot(tX - px, tY - py); // if (d < bestD) { // bestD = d; // best = i; // } // } // tramSegIndex = best; // } // } // } // const vertexCount = routePath.length / 2; // if (vertexCount > 1) { // const generateThickLine = (points: Float32Array, width: number) => { // const vertices: number[] = []; // const halfWidth = width / 2; // if (points.length < 4) return new Float32Array(); // for (let i = 0; i < points.length - 2; i += 2) { // const x1 = points[i]; // const y1 = points[i + 1]; // const x2 = points[i + 2]; // const y2 = points[i + 3]; // const dx = x2 - x1; // const dy = y2 - y1; // const length = Math.sqrt(dx * dx + dy * dy); // if (length === 0) continue; // const perpX = (-dy / length) * halfWidth; // const perpY = (dx / length) * halfWidth; // vertices.push(x1 + perpX, y1 + perpY); // vertices.push(x1 - perpX, y1 - perpY); // vertices.push(x2 + perpX, y2 + perpY); // vertices.push(x1 - perpX, y1 - perpY); // vertices.push(x2 - perpX, y2 - perpY); // vertices.push(x2 + perpX, y2 + perpY); // if (i < points.length - 4) { // const x3 = points[i + 4]; // const y3 = points[i + 5]; // const dx2 = x3 - x2; // const dy2 = y3 - y2; // const length2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); // if (length2 > 0) { // const perpX2 = (-dy2 / length2) * halfWidth; // const perpY2 = (dx2 / length2) * halfWidth; // vertices.push(x2 + perpX, y2 + perpY); // vertices.push(x2 - perpX, y2 - perpY); // vertices.push(x2 + perpX2, y2 + perpY2); // vertices.push(x2 - perpX, y2 - perpY); // vertices.push(x2 - perpX2, y2 - perpY2); // vertices.push(x2 + perpX2, y2 + perpY2); // } // } // } // return new Float32Array(vertices); // }; // const lineWidth = Math.min(6); // const r1 = ((PATH_COLOR >> 16) & 0xff) / 255; // const g1 = ((PATH_COLOR >> 8) & 0xff) / 255; // const b1 = (PATH_COLOR & 0xff) / 255; // gl.uniform4f(uniforms.u_color, r1, g1, b1, 1); // if (tramSegIndex >= 0) { // const animatedPos = animatedYellowDotPosition; // if ( // animatedPos && // animatedPos.x !== undefined && // animatedPos.y !== undefined // ) { // const passedPoints: number[] = []; // for (let i = 0; i <= tramSegIndex; i++) { // passedPoints.push(routePath[i * 2], routePath[i * 2 + 1]); // } // passedPoints.push(animatedPos.x, animatedPos.y); // if (passedPoints.length >= 4) { // const thickLineVertices = generateThickLine( // new Float32Array(passedPoints), // lineWidth // ); // gl.bufferData(gl.ARRAY_BUFFER, thickLineVertices, gl.STATIC_DRAW); // gl.drawArrays(gl.TRIANGLES, 0, thickLineVertices.length / 2); // } // } // } // const r2 = ((UNPASSED_STATION_COLOR >> 16) & 0xff) / 255; // const g2 = ((UNPASSED_STATION_COLOR >> 8) & 0xff) / 255; // const b2 = (UNPASSED_STATION_COLOR & 0xff) / 255; // gl.uniform4f(uniforms.u_color, r2, g2, b2, 1); // const animatedPos = animatedYellowDotPosition; // if ( // animatedPos && // animatedPos.x !== undefined && // animatedPos.y !== undefined // ) { // const unpassedPoints: number[] = []; // unpassedPoints.push(animatedPos.x, animatedPos.y); // for (let i = tramSegIndex + 1; i < vertexCount; i++) { // unpassedPoints.push(routePath[i * 2], routePath[i * 2 + 1]); // } // if (unpassedPoints.length >= 4) { // const thickLineVertices = generateThickLine( // new Float32Array(unpassedPoints), // lineWidth // ); // gl.bufferData(gl.ARRAY_BUFFER, thickLineVertices, gl.STATIC_DRAW); // gl.drawArrays(gl.TRIANGLES, 0, thickLineVertices.length / 2); // } // } // } // if (stationPoints.length > 0) { // gl.useProgram(pprog); // const a_pos_pts = gl.getAttribLocation(pprog, "a_pos"); // const u_cameraPos_pts = gl.getUniformLocation(pprog, "u_cameraPos"); // const u_scale_pts = gl.getUniformLocation(pprog, "u_scale"); // const u_resolution_pts = gl.getUniformLocation(pprog, "u_resolution"); // const u_pointSize = gl.getUniformLocation(pprog, "u_pointSize"); // const u_color_pts = gl.getUniformLocation(pprog, "u_color"); // gl.uniform2f(u_cameraPos_pts, position.x, position.y); // gl.uniform1f(u_scale_pts, scale); // gl.uniform2f(u_resolution_pts, canvas.width, canvas.height); // gl.bindBuffer(gl.ARRAY_BUFFER, pbuffer); // gl.bufferData(gl.ARRAY_BUFFER, stationPoints, gl.STATIC_DRAW); // gl.enableVertexAttribArray(a_pos_pts); // gl.vertexAttribPointer(a_pos_pts, 2, gl.FLOAT, false, 0, 0); // gl.uniform1f(u_pointSize, 10 * scale * 1.5); // const r_outline = ((BACKGROUND_COLOR >> 16) & 0xff) / 255; // const g_outline = ((BACKGROUND_COLOR >> 8) & 0xff) / 255; // const b_outline = (BACKGROUND_COLOR & 0xff) / 255; // gl.uniform4f(u_color_pts, r_outline, g_outline, b_outline, 1); // gl.drawArrays(gl.POINTS, 0, stationPoints.length / 2); // gl.uniform1f(u_pointSize, 8.0 * scale * 1.5); // if (tramSegIndex >= 0) { // const passedStations = []; // for (let i = 0; i < stationData.length; i++) { // if (i <= tramSegIndex) { // passedStations.push(stationPoints[i * 2], stationPoints[i * 2 + 1]); // } // } // if (passedStations.length > 0) { // const r_passed = ((PATH_COLOR >> 16) & 0xff) / 255; // const g_passed = ((PATH_COLOR >> 8) & 0xff) / 255; // const b_passed = (PATH_COLOR & 0xff) / 255; // gl.uniform4f(u_color_pts, r_passed, g_passed, b_passed, 1); // gl.bufferData( // gl.ARRAY_BUFFER, // new Float32Array(passedStations), // gl.STATIC_DRAW // ); // gl.drawArrays(gl.POINTS, 0, passedStations.length / 2); // } // } // if (tramSegIndex >= 0) { // const unpassedStations = []; // for (let i = 0; i < stationData.length; i++) { // if (i > tramSegIndex) { // unpassedStations.push( // stationPoints[i * 2], // stationPoints[i * 2 + 1] // ); // } // } // if (unpassedStations.length > 0) { // const r_unpassed = ((UNPASSED_STATION_COLOR >> 16) & 0xff) / 255; // const g_unpassed = ((UNPASSED_STATION_COLOR >> 8) & 0xff) / 255; // const b_unpassed = (UNPASSED_STATION_COLOR & 0xff) / 255; // gl.uniform4f(u_color_pts, r_unpassed, g_unpassed, b_unpassed, 1); // gl.bufferData( // gl.ARRAY_BUFFER, // new Float32Array(unpassedStations), // gl.STATIC_DRAW // ); // gl.drawArrays(gl.POINTS, 0, unpassedStations.length / 2); // } // } else { // const r_unpassed = ((UNPASSED_STATION_COLOR >> 16) & 0xff) / 255; // const g_unpassed = ((UNPASSED_STATION_COLOR >> 8) & 0xff) / 255; // const b_unpassed = (UNPASSED_STATION_COLOR & 0xff) / 255; // gl.uniform4f(u_color_pts, r_unpassed, g_unpassed, b_unpassed, 1); // gl.bufferData(gl.ARRAY_BUFFER, stationPoints, gl.STATIC_DRAW); // gl.drawArrays(gl.POINTS, 0, stationPoints.length / 2); // } // } // const a_pos_pts = gl.getAttribLocation(pprog, "a_pos"); // const u_cameraPos_pts = gl.getUniformLocation(pprog, "u_cameraPos"); // const u_scale_pts = gl.getUniformLocation(pprog, "u_scale"); // const u_resolution_pts = gl.getUniformLocation(pprog, "u_resolution"); // const u_pointSize = gl.getUniformLocation(pprog, "u_pointSize"); // const u_color_pts = gl.getUniformLocation(pprog, "u_color"); // gl.uniform2f(u_cameraPos_pts, position.x, position.y); // gl.uniform1f(u_scale_pts, scale); // gl.uniform2f(u_resolution_pts, canvas.width, canvas.height); // 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); // } // } // const passedStations: number[] = []; // const unpassedStations: number[] = []; // for (let i = 0; i < stationPoints.length; i += 2) { // const sx = stationPoints[i], // sy = stationPoints[i + 1]; // const seg = getSeg(sx, sy); // if (tramSegForStations !== -1 && seg !== -1 && seg < tramSegForStations) // passedStations.push(sx, sy); // else unpassedStations.push(sx, sy); // } // const outlineSize = 10.0 * scale * 2, // coreSize = 8.0 * scale * 2; // gl.bindBuffer(gl.ARRAY_BUFFER, pbuffer); // gl.bufferData( // gl.ARRAY_BUFFER, // toPointsArray(unpassedStations), // gl.STREAM_DRAW // ); // gl.enableVertexAttribArray(a_pos_pts); // gl.vertexAttribPointer(a_pos_pts, 2, gl.FLOAT, false, 0, 0); // gl.uniform1f(u_pointSize, outlineSize); // gl.uniform4f( // u_color_pts, // ((BACKGROUND_COLOR >> 16) & 255) / 255, // ((BACKGROUND_COLOR >> 8) & 255) / 255, // (BACKGROUND_COLOR & 255) / 255, // 1 // ); // if (unpassedStations.length) // gl.drawArrays(gl.POINTS, 0, unpassedStations.length / 2); // gl.uniform1f(u_pointSize, coreSize); // gl.uniform4f( // u_color_pts, // ((UNPASSED_STATION_COLOR >> 16) & 255) / 255, // ((UNPASSED_STATION_COLOR >> 8) & 255) / 255, // (UNPASSED_STATION_COLOR & 255) / 255, // 1 // ); // if (unpassedStations.length) // gl.drawArrays(gl.POINTS, 0, unpassedStations.length / 2); // gl.bindBuffer(gl.ARRAY_BUFFER, pbuffer); // gl.bufferData( // gl.ARRAY_BUFFER, // toPointsArray(passedStations), // gl.STREAM_DRAW // ); // gl.enableVertexAttribArray(a_pos_pts); // gl.vertexAttribPointer(a_pos_pts, 2, gl.FLOAT, false, 0, 0); // gl.uniform1f(u_pointSize, outlineSize); // gl.uniform4f( // u_color_pts, // ((BACKGROUND_COLOR >> 16) & 255) / 255, // ((BACKGROUND_COLOR >> 8) & 255) / 255, // (BACKGROUND_COLOR & 255) / 255, // 1 // ); // if (passedStations.length) // gl.drawArrays(gl.POINTS, 0, passedStations.length / 2); // gl.uniform1f(u_pointSize, coreSize); // gl.uniform4f( // u_color_pts, // ((PATH_COLOR >> 16) & 255) / 255, // ((PATH_COLOR >> 8) & 255) / 255, // (PATH_COLOR & 255) / 255, // 1 // ); // if (passedStations.length) // gl.drawArrays(gl.POINTS, 0, passedStations.length / 2); // if ( // stationData && // stationData.length > 0 && // routeData && // apiStore?.context // ) { // const centerLat = routeData.center_latitude; // const centerLon = routeData.center_longitude; // if (centerLat !== undefined && centerLon !== undefined) { // const cos = Math.cos(rotationAngle); // const sin = Math.sin(rotationAngle); // const startStationData = stationData.find( // (station) => station.id.toString() === apiStore.context?.startStopId // ); // const endStationData = stationData.find( // (station) => station.id.toString() === apiStore.context?.endStopId // ); // const terminalStations: number[] = []; // if (startStationData) { // const startLocal = coordinatesToLocal( // startStationData.latitude - centerLat, // startStationData.longitude - centerLon // ); // const startX = startLocal.x * UP_SCALE; // const startY = startLocal.y * UP_SCALE; // const startRx = startX * cos - startY * sin; // const startRy = startX * sin + startY * cos; // terminalStations.push(startRx, startRy); // } // if (endStationData) { // const endLocal = coordinatesToLocal( // endStationData.latitude - centerLat, // endStationData.longitude - centerLon // ); // const endX = endLocal.x * UP_SCALE; // const endY = endLocal.y * UP_SCALE; // const endRx = endX * cos - endY * sin; // const endRy = endX * sin + endY * cos; // terminalStations.push(endRx, endRy); // } // if (terminalStations.length > 0) { // const terminalStationData: any[] = []; // if (startStationData) terminalStationData.push(startStationData); // if (endStationData) terminalStationData.push(endStationData); // let tramSegIndex = -1; // const coords: any = apiStore?.context?.currentCoordinates; // if (coords && centerLat !== undefined && centerLon !== undefined) { // const local = coordinatesToLocal( // coords.latitude - centerLat, // coords.longitude - centerLon // ); // const wx = local.x * UP_SCALE; // const wy = local.y * UP_SCALE; // const cosR = Math.cos(rotationAngle); // const sinR = Math.sin(rotationAngle); // const tx = wx * cosR - wy * sinR; // const ty = wx * sinR + wy * cosR; // let best = -1; // let bestD = Infinity; // for (let i = 0; i < routePath.length - 2; i += 2) { // const p1x = routePath[i]; // const p1y = routePath[i + 1]; // const p2x = routePath[i + 2]; // const p2y = routePath[i + 3]; // const dx = p2x - p1x; // const dy = p2y - p1y; // const len2 = dx * dx + dy * dy; // if (!len2) continue; // const t = ((tx - p1x) * dx + (ty - p1y) * dy) / len2; // const cl = Math.max(0, Math.min(1, t)); // const px = p1x + cl * dx; // const py = p1y + cl * dy; // const d = Math.hypot(tx - px, ty - py); // if (d < bestD) { // bestD = d; // best = i / 2; // } // } // tramSegIndex = best; // } // const isStartPassed = startStationData // ? (() => { // const sx = terminalStations[0]; // const sy = terminalStations[1]; // const seg = (() => { // if (routePath.length < 4) return -1; // let best = -1; // let bestD = Infinity; // for (let i = 0; i < routePath.length - 2; i += 2) { // const p1x = routePath[i]; // const p1y = routePath[i + 1]; // const p2x = routePath[i + 2]; // const p2y = routePath[i + 3]; // const dx = p2x - p1x; // const dy = p2y - p1y; // const len2 = dx * dx + dy * dy; // if (!len2) continue; // const t = ((sx - p1x) * dx + (sy - p1y) * dy) / len2; // const cl = Math.max(0, Math.min(1, t)); // const px = p1x + cl * dx; // const py = p1y + cl * dy; // const d = Math.hypot(sx - px, sy - py); // if (d < bestD) { // bestD = d; // best = i / 2; // } // } // return best; // })(); // return tramSegIndex !== -1 && seg !== -1 && seg < tramSegIndex; // })() // : false; // const isEndPassed = endStationData // ? (() => { // const ex = terminalStations[terminalStations.length - 2]; // const ey = terminalStations[terminalStations.length - 1]; // const seg = (() => { // if (routePath.length < 4) return -1; // let best = -1; // let bestD = Infinity; // for (let i = 0; i < routePath.length - 2; i += 2) { // const p1x = routePath[i]; // const p1y = routePath[i + 1]; // const p2x = routePath[i + 2]; // const p2y = routePath[i + 3]; // const dx = p2x - p1x; // const dy = p2y - p1y; // const len2 = dx * dx + dy * dy; // if (!len2) continue; // const t = ((ex - p1x) * dx + (ey - p1y) * dy) / len2; // const cl = Math.max(0, Math.min(1, t)); // const px = p1x + cl * dx; // const py = p1y + cl * dy; // const d = Math.hypot(ex - px, ey - py); // if (d < bestD) { // bestD = d; // best = i / 2; // } // } // return best; // })(); // return tramSegIndex !== -1 && seg !== -1 && seg < tramSegIndex; // })() // : false; // gl.bindBuffer(gl.ARRAY_BUFFER, pbuffer); // gl.bufferData( // gl.ARRAY_BUFFER, // new Float32Array(terminalStations), // gl.STREAM_DRAW // ); // gl.enableVertexAttribArray(a_pos_pts); // gl.vertexAttribPointer(a_pos_pts, 2, gl.FLOAT, false, 0, 0); // gl.uniform1f(u_pointSize, 18.0 * scale); // if (startStationData && endStationData) { // if (isStartPassed) { // gl.uniform4f(u_color_pts, 1.0, 0.4, 0.4, 1.0); // } else { // gl.uniform4f(u_color_pts, 0.7, 0.7, 0.7, 1.0); // } // gl.drawArrays(gl.POINTS, 0, 1); // if (isEndPassed) { // gl.uniform4f(u_color_pts, 1.0, 0.4, 0.4, 1.0); // } else { // gl.uniform4f(u_color_pts, 0.7, 0.7, 0.7, 1.0); // } // gl.drawArrays(gl.POINTS, 1, 1); // } else { // const isPassed = startStationData ? isStartPassed : isEndPassed; // if (isPassed) { // gl.uniform4f(u_color_pts, 1.0, 0.4, 0.4, 1.0); // } else { // gl.uniform4f(u_color_pts, 0.7, 0.7, 0.7, 1.0); // } // gl.drawArrays(gl.POINTS, 0, terminalStations.length / 2); // } // gl.uniform1f(u_pointSize, 11.0 * scale); // const r_center = ((BACKGROUND_COLOR >> 16) & 0xff) / 255; // const g_center = ((BACKGROUND_COLOR >> 8) & 0xff) / 255; // const b_center = (BACKGROUND_COLOR & 0xff) / 255; // gl.uniform4f(u_color_pts, r_center, g_center, b_center, 1.0); // gl.drawArrays(gl.POINTS, 0, terminalStations.length / 2); // } // } // } // if (animatedYellowDotPosition) { // const rx = animatedYellowDotPosition.x; // const ry = animatedYellowDotPosition.y; // gl.uniform1f(u_pointSize, 13.3333 * scale); // gl.uniform4f(u_color_pts, 1.0, 1.0, 0.0, 1.0); // const tmp = new Float32Array([rx, ry]); // gl.bindBuffer(gl.ARRAY_BUFFER, pbuffer); // gl.bufferData(gl.ARRAY_BUFFER, tmp, gl.STREAM_DRAW); // gl.enableVertexAttribArray(a_pos_pts); // gl.vertexAttribPointer(a_pos_pts, 2, gl.FLOAT, false, 0, 0); // gl.drawArrays(gl.POINTS, 0, 1); // } // }, [ // routePath, // stationPoints, // sightPoints, // position.x, // position.y, // scale, // animatedYellowDotPosition?.x, // animatedYellowDotPosition?.y, // ]); // useEffect(() => { // const canvas = canvasRef.current; // if (!canvas) return; // if (!routePath || routePath.length < 4) return; // let minX = Infinity, // minY = Infinity, // maxX = -Infinity, // maxY = -Infinity; // for (let i = 0; i < routePath.length; i += 2) { // const x = routePath[i]; // const y = routePath[i + 1]; // if (x < minX) minX = x; // if (y < minY) minY = y; // if (x > maxX) maxX = x; // if (y > maxY) maxY = y; // } // if ( // !isFinite(minX) || // !isFinite(minY) || // !isFinite(maxX) || // !isFinite(maxY) // ) // return; // const worldWidth = Math.max(1, maxX - minX); // const worldHeight = Math.max(1, maxY - minY); // const margin = 0.1; // const targetScale = Math.min( // (canvas.width * (1 - margin)) / worldWidth, // (canvas.height * (1 - margin)) / worldHeight // ); // const centerX = (minX + maxX) / 2; // const centerY = (minY + maxY) / 2; // const clampedScale = clampScale(targetScale); // setScaleRef.current(clampedScale); // setPositionRef.current({ // x: canvas.width / 2 - centerX * clampedScale, // y: canvas.height / 2 - centerY * clampedScale, // }); // }, [routePath]); // useEffect(() => { // const canvas = canvasRef.current; // if (!canvas) return; // let isDragging = false; // let startMouse = { x: 0, y: 0 }; // let startPos = { x: 0, y: 0 }; // const activePointers = new Map(); // let isPinching = false; // let pinchStart: { // distance: number; // midpoint: { x: number; y: number }; // scale: number; // position: { x: number; y: number }; // } | null = null; // const getDistance = ( // p1: { x: number; y: number }, // p2: { x: number; y: number } // ) => Math.hypot(p2.x - p1.x, p2.y - p1.y); // const getMidpoint = ( // p1: { x: number; y: number }, // p2: { x: number; y: number } // ) => ({ // x: (p1.x + p2.x) / 2, // y: (p1.y + p2.y) / 2, // }); // const onPointerDown = (e: PointerEvent) => { // updateUserActivity(); // if (isAutoMode) { // setIsAutoMode(false); // } // cameraAnimationStore.stopAnimation(); // canvas.setPointerCapture(e.pointerId); // const rect = canvas.getBoundingClientRect(); // activePointers.set(e.pointerId, { // x: e.clientX - rect.left, // y: e.clientY - rect.top, // }); // if (activePointers.size === 1) { // isDragging = true; // startMouse = { x: e.clientX - rect.left, y: e.clientY - rect.top }; // startPos = { x: positionRef.current.x, y: positionRef.current.y }; // } else if (activePointers.size === 2) { // isDragging = false; // const [p1, p2] = Array.from(activePointers.values()); // pinchStart = { // distance: getDistance(p1, p2), // midpoint: getMidpoint(p1, p2), // scale: scaleRef.current, // position: { x: positionRef.current.x, y: positionRef.current.y }, // }; // isPinching = true; // } // }; // const onPointerMove = (e: PointerEvent) => { // if (!activePointers.has(e.pointerId)) return; // updateUserActivity(); // const rect = canvas.getBoundingClientRect(); // activePointers.set(e.pointerId, { // x: e.clientX - rect.left, // y: e.clientY - rect.top, // }); // if (activePointers.size === 2) { // isDragging = false; // const pointersArray = Array.from(activePointers.values()); // if (pointersArray.length === 2) { // const [p1, p2] = pointersArray; // if (!isPinching || pinchStart === null) { // isPinching = true; // pinchStart = { // distance: getDistance(p1, p2), // midpoint: getMidpoint(p1, p2), // scale: scaleRef.current, // position: { x: positionRef.current.x, y: positionRef.current.y }, // }; // } // if (pinchStart) { // const currentDistance = getDistance(p1, p2); // const zoomFactor = currentDistance / pinchStart.distance; // const unclampedScale = pinchStart.scale * zoomFactor; // const newScale = clampScale(Math.max(0.1, unclampedScale)); // const k = newScale / pinchStart.scale; // const newPosition = { // x: pinchStart.position.x * k + pinchStart.midpoint.x * (1 - k), // y: pinchStart.position.y * k + pinchStart.midpoint.y * (1 - k), // }; // setPositionRef.current(newPosition); // setScaleRef.current(newScale); // } // } // } else if (isDragging && activePointers.size === 1) { // const p = Array.from(activePointers.values())[0]; // if ( // !startMouse || // !startPos || // typeof startMouse.x !== "number" || // typeof startMouse.y !== "number" || // typeof startPos.x !== "number" || // typeof startPos.y !== "number" // ) { // console.warn( // "WebGLMap: Некорректные значения startMouse или startPos:", // { // startMouse, // startPos, // p, // } // ); // return; // } // const dx = p.x - startMouse.x; // const dy = p.y - startMouse.y; // setPositionRef.current({ x: startPos.x + dx, y: startPos.y + dy }); // } // }; // const onPointerUp = (e: PointerEvent) => { // updateUserActivity(); // canvas.releasePointerCapture(e.pointerId); // activePointers.delete(e.pointerId); // if (activePointers.size < 2) { // isPinching = false; // pinchStart = null; // } // if (activePointers.size === 0) { // isDragging = false; // } else if (activePointers.size === 1) { // const p = Array.from(activePointers.values())[0]; // startPos = { x: positionRef.current.x, y: positionRef.current.y }; // startMouse = { x: p.x, y: p.y }; // isDragging = true; // } // }; // const onPointerCancel = (e: PointerEvent) => { // updateUserActivity(); // canvas.releasePointerCapture(e.pointerId); // activePointers.delete(e.pointerId); // isPinching = false; // pinchStart = null; // if (activePointers.size === 0) { // isDragging = false; // } // }; // const onWheel = (e: WheelEvent) => { // e.preventDefault(); // updateUserActivity(); // if (isAutoMode) { // setIsAutoMode(false); // } // cameraAnimationStore.stopAnimation(); // const rect = canvas.getBoundingClientRect(); // const mouseX = // (e.clientX - rect.left) * (canvas.width / canvas.clientWidth); // const mouseY = // (e.clientY - rect.top) * (canvas.height / canvas.clientHeight); // const delta = e.deltaY > 0 ? 0.9 : 1.1; // const unclampedScale = scaleRef.current * delta; // const newScale = clampScale(Math.max(0.1, unclampedScale)); // const k = newScale / scaleRef.current; // const newPosition = { // x: positionRef.current.x * k + mouseX * (1 - k), // y: positionRef.current.y * k + mouseY * (1 - k), // }; // setScaleRef.current(newScale); // setPositionRef.current(newPosition); // }; // canvas.addEventListener("pointerdown", onPointerDown); // canvas.addEventListener("pointermove", onPointerMove); // canvas.addEventListener("pointerup", onPointerUp); // canvas.addEventListener("pointercancel", onPointerCancel); // canvas.addEventListener("pointerleave", onPointerUp); // canvas.addEventListener("wheel", onWheel, { passive: false }); // return () => { // canvas.removeEventListener("pointerdown", onPointerDown); // canvas.removeEventListener("pointermove", onPointerMove); // canvas.removeEventListener("pointerup", onPointerUp); // canvas.removeEventListener("pointercancel", onPointerCancel); // canvas.removeEventListener("pointerleave", onPointerUp); // canvas.removeEventListener("wheel", onWheel as any); // }; // }, [ // updateUserActivity, // setIsAutoMode, // cameraAnimationStore, // isAutoMode, // clampScale, // ]); // return ( //
// //
// {stationLabels.map((l, idx) => ( //
//
{l.name}
// {l.sub ? ( //
// {l.sub} //
// ) : null} //
// ))} // {sightData?.map((s: any, i: number) => { // const centerLat = routeData?.center_latitude; // const centerLon = routeData?.center_longitude; // if (centerLat === undefined || centerLon === undefined) return null; // const cos = Math.cos(rotationAngle); // const sin = Math.sin(rotationAngle); // const local = coordinatesToLocal( // s.latitude - centerLat, // s.longitude - centerLon // ); // const x = local.x * UP_SCALE; // const y = local.y * UP_SCALE; // const rx = x * cos - y * sin; // const ry = x * sin + y * cos; // const dpr = Math.max( // 1, // (typeof window !== "undefined" && window.devicePixelRatio) || 1 // ); // const sx = (rx * scale + position.x) / dpr; // const sy = (ry * scale + position.y) / dpr; // const size = 30; // const handleSightClick = () => { // const { // setSelectedSightId, // setIsManualSelection, // setIsRightWidgetSelectorOpen, // closeGovernorModal, // } = useGeolocationStore(); // setSelectedSightId(String(s.id)); // setIsManualSelection(true); // setIsRightWidgetSelectorOpen(false); // closeGovernorModal(); // }; // return ( // // ); // })} // {(() => { // if (!routeData) return null; // const centerLat = routeData.center_latitude; // const centerLon = routeData.center_longitude; // if (centerLat === undefined || centerLon === undefined) return null; // const coords: any = apiStore?.context?.currentCoordinates; // if (!coords) return null; // const local = coordinatesToLocal( // coords.latitude - centerLat, // coords.longitude - centerLon // ); // const wx = local.x * UP_SCALE; // const wy = local.y * UP_SCALE; // const cosR = Math.cos(rotationAngle); // const sinR = Math.sin(rotationAngle); // const rx = wx * cosR - wy * sinR; // const ry = wx * sinR + wy * cosR; // const dpr2 = Math.max( // 1, // (typeof window !== "undefined" && window.devicePixelRatio) || 1 // ); // const screenX = (rx * scale + position.x) / dpr2; // const screenY = (ry * scale + position.y) / dpr2; // 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 stationsForAngle = (stationData || []).map((st: any) => { // const loc = coordinatesToLocal( // st.latitude - centerLat, // st.longitude - centerLon // ); // const x = loc.x * UP_SCALE, // y = loc.y * UP_SCALE; // const rx2 = x * cosR - y * sinR, // ry2 = x * sinR + y * cosR; // return { // longitude: rx2, // latitude: ry2, // offset_x: st.offset_x, // offset_y: st.offset_y, // }; // }); // let tramSegIndex = -1; // if (routePath.length >= 4) { // let best = -1, // bestD = Infinity; // for (let i = 0; i < routePath.length - 2; i += 2) { // const p1x = routePath[i], // p1y = routePath[i + 1]; // const p2x = routePath[i + 2], // p2y = routePath[i + 3]; // const dx = p2x - p1x, // dy = p2y - p1y; // const len2 = dx * dx + dy * dy; // if (!len2) continue; // const t = ((rx - p1x) * dx + (ry - p1y) * dy) / len2; // const cl = Math.max(0, Math.min(1, t)); // const px = p1x + cl * dx, // py = p1y + cl * dy; // const d = Math.hypot(rx - px, ry - py); // if (d < bestD) { // bestD = d; // best = i / 2; // } // } // tramSegIndex = best; // } // const optimalAngle = (() => { // const testRadiusInMap = 100 / scale; // const minPath = 60, // minPassed = 60, // minStation = 50; // let bestAng = 0, // bestScore = Infinity; // for (let i = 0; i < 12; i++) { // const ang = (i * Math.PI * 2) / 12; // const tx = rx + Math.cos(ang) * testRadiusInMap; // const ty = ry + Math.sin(ang) * testRadiusInMap; // const distPath = (function () { // if (pathPts.length < 2) return Infinity; // let md = Infinity; // for (let k = 0; k < pathPts.length - 1; k++) { // const p1 = pathPts[k], // p2 = pathPts[k + 1]; // const L2 = (p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2; // if (!L2) continue; // const tt = // ((tx - p1.x) * (p2.x - p1.x) + // (ty - p1.y) * (p2.y - p1.y)) / // L2; // const cl = Math.max(0, Math.min(1, tt)); // const px = p1.x + cl * (p2.x - p1.x), // py = p1.y + cl * (p2.y - p1.y); // const d = Math.hypot(tx - px, ty - py); // if (d < md) md = d; // } // return md * scale; // })(); // const distPassed = (function () { // if (pathPts.length < 2 || tramSegIndex < 0) return Infinity; // let md = Infinity; // for ( // let k = 0; // k <= Math.min(tramSegIndex, pathPts.length - 2); // k++ // ) { // const p1 = pathPts[k], // p2 = pathPts[k + 1]; // const L2 = (p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2; // if (!L2) continue; // const tt = // ((tx - p1.x) * (p2.x - p1.x) + // (ty - p1.y) * (p2.y - p1.y)) / // L2; // const cl = Math.max(0, Math.min(1, tt)); // const px = p1.x + cl * (p2.x - p1.x), // py = p1.y + cl * (p2.y - p1.y); // const d = Math.hypot(tx - px, ty - py); // if (d < md) md = d; // } // return md * scale; // })(); // const distStation = (function () { // if (!stationsForAngle.length) return Infinity; // const DEFAULT_LABEL_OFFSET_X = 25, // DEFAULT_LABEL_OFFSET_Y = 0; // let md = Infinity; // for (const st of stationsForAngle) { // const offsetX = // st.offset_x === 0 && st.offset_y === 0 // ? DEFAULT_LABEL_OFFSET_X // : st.offset_x || 0 * 3; // const offsetY = // st.offset_x === 0 && st.offset_y === 0 // ? DEFAULT_LABEL_OFFSET_Y // : st.offset_y || 0 * 3; // const lx = st.longitude + offsetX, // ly = st.latitude + offsetY; // const d = Math.hypot(tx - lx, ty - ly); // if (d < md) md = d; // } // return md * scale; // })(); // let weight = 0; // if (distPath < minPath) weight += 100 * (1 - distPath / minPath); // if (distPassed < minPassed) // weight += 10 * (1 - distPassed / minPassed); // if (distStation < minStation) // weight += 1000 * (1 - distStation / minStation); // if (weight < bestScore) { // bestScore = weight; // bestAng = ang; // } // } // return bestAng; // })(); // return ( // // ); // })()} //
//
// ); // }); // export default WebGLMap;