Flex :给LineChart设置坐标轴最大最小范围

本文介绍了一个使用Adobe Flex创建的图表应用程序实例。该程序利用LineChart组件展示了一段时间内的股票价格变动情况,并通过调整滑块来改变图表显示的价格区间。此外,还实现了货币格式化的y轴标签显示。


复制代码<?xml version="1.0"?> 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
        layout="vertical" 
        verticalAlign="middle" 
        backgroundColor="white" 
        preinitialize="init();"> 
  
    <mx:Script> 
        <![CDATA[ 
            import mx.charts.chartClasses.IAxis; 
            import mx.formatters.CurrencyFormatter; 
  
            private var currFormatter:CurrencyFormatter; 
  
            private function init():void { 
                currFormatter = new CurrencyFormatter(); 
                currFormatter.precision = 2; 
            } 
  
            private function linearAxis_labelFunc(item:Object, prevValue:Object, axis:IAxis):String { 
                return currFormatter.format(item); 
            } 
  
            private function lineChart_creationComplete():void { 
                var linearAxisMinimum:int = Math.floor(linearAxis.computedMinimum); 
                var linearAxisMaximum:int = Math.ceil(linearAxis.computedMaximum); 
                linearAxis.minimum = linearAxisMinimum; 
                linearAxis.maximum = linearAxisMaximum; 
            } 
        ]]> 
    </mx:Script> 
  
    <mx:XMLListCollection id="dp"> 
        <mx:source> 
            <mx:XMLList> 
                <quote date="8/27/2007" open="40.38" close="40.81" /> 
                <quote date="8/24/2007" open="40.5" close="40.41" /> 
                <quote date="8/23/2007" open="40.82" close="40.6" /> 
                <quote date="8/22/2007" open="40.4" close="40.77" /> 
                <quote date="8/21/2007" open="40.41" close="40.13" /> 
                <quote date="8/20/2007" open="40.55" close="40.74" /> 
                <quote date="8/17/2007" open="40.18" close="40.32" /> 
                <quote date="8/16/2007" open="39.83" close="39.96" /> 
                <quote date="8/15/2007" open="40.22" close="40.18" /> 
                <quote date="8/14/2007" open="41.01" close="40.41" /> 
                <quote date="8/13/2007" open="41" close="40.83" /> 
                <quote date="8/10/2007" open="41.3" close="41.06" /> 
                <quote date="8/9/2007" open="39.9" close="40.75" /> 
                <quote date="8/8/2007" open="39.61" close="40.23" /> 
                <quote date="8/7/2007" open="39.08" close="39.42" /> 
                <quote date="8/6/2007" open="38.71" close="39.38" /> 
                <quote date="8/3/2007" open="39.47" close="38.75" /> 
                <quote date="8/2/2007" open="39.4" close="39.52" /> 
                <quote date="8/1/2007" open="40.29" close="39.58" /> 
            </mx:XMLList> 
        </mx:source> 
    </mx:XMLListCollection> 
  
    <mx:ApplicationControlBar dock="true"> 
        <mx:Form styleName="plain"> 
            <mx:FormItem label="minimum:"> 
                <mx:HSlider id="minSlider" 
                        minimum="28" 
                        maximum="38" 
                        value="38" 
                        liveDragging="true" 
                        snapInterval="1" 
                        change="linearAxis.minimum = event.value;" /> 
            </mx:FormItem> 
            <mx:FormItem label="maximum:"> 
                <mx:HSlider id="maxSlider" 
                        minimum="42" 
                        maximum="52" 
                        value="42" 
                        liveDragging="true" 
                        snapInterval="1" 
                        change="linearAxis.maximum = event.value;" /> 
            </mx:FormItem> 
        </mx:Form> 
  
        <mx:Spacer width="100%" /> 
  
        <mx:Legend dataProvider="{lineChart}" 
                direction="horizontal" /> 
    </mx:ApplicationControlBar> 
  
    <mx:LineChart id="lineChart" 
            showDataTips="true" 
            dataProvider="{dp}" 
            width="100%" 
            height="100%" 
            creationComplete="lineChart_creationComplete();"> 
  
        <mx:backgroundElements> 
            <mx:GridLines id="gridLines" 
                    direction="both" 
                    verticalTickAligned="false"> 
                <mx:verticalStroke> 
                    <mx:Stroke color="haloSilver" 
                            weight="0" 
                            alpha="1.0" /> 
                </mx:verticalStroke> 
                <mx:horizontalStroke> 
                    <!-- Set alpha to 0 so stroke isn't visible. --> 
                    <mx:Stroke color="white" 
                            weight="0" 
                            alpha="0.0" /> 
                </mx:horizontalStroke> 
                <mx:horizontalFill> 
                    <mx:SolidColor color="haloSilver" 
                            alpha="0.1" /> 
                </mx:horizontalFill> 
            </mx:GridLines> 
        </mx:backgroundElements> 
  
        <!-- vertical axis ** minorInterval:y轴线条的间隔  interval:y轴数值的间隔,maximum:y轴最大值 , minimum:y轴最小值 --> 
        <mx:verticalAxis> 
            <mx:LinearAxis id="linearAxis" 
                    baseAtZero="false" 
                    title="Price (USD)" 
                    minorInterval="0.10" 
                    interval="0.5" 
                     maximum="78" minimum="0"
                    labelFunction="linearAxis_labelFunc" /> 
        </mx:verticalAxis> 
  
        <!-- horizontal axis --> 
        <mx:horizontalAxis> 
            <mx:CategoryAxis id="ca" 
                    categoryField="@date" 
                    title="Date" /> 
        </mx:horizontalAxis> 
  
        <!-- horizontal axis renderer --> 
        <mx:horizontalAxisRenderers> 
            <mx:AxisRenderer axis="{ca}" 
                    canDropLabels="true" /> 
        </mx:horizontalAxisRenderers> 
  
        <!-- series --> 
        <mx:series> 
            <mx:LineSeries yField="@open" 
                    displayName="Open" /> 
        </mx:series> 
    </mx:LineChart> 
  
</mx:Application>
复制代码


function initChart() { let series = []; let yAxis = []; let timeList = []; if (propertyValues.value) { Object.keys(propertyValues.value).forEach((key, index) => { let timeArr = propertyValues.value[key].map(item => new Date(item.localDateTime).getTime() ); timeList = timeList.length > timeArr.length ? timeList : timeArr; series.push({ name: findPropertyName(key), showSymbol: false, type: "line", data: propertyValues.value[key].map(item => [ item.localDateTime, item.value ]) }); }); yAxis.push({ name: transformI18n($t("IOT.purePropertyValue")), type: "value", axisLine: { show: true, symbol: ["none", "arrow"], // 显示线的起始箭头,不显示结束箭头 lineStyle: { color: "rgba(117,127,158,0.8)" } }, axisTick: { show: false } }); } if (boolCollection.value) { Object.keys(boolCollection.value).forEach(key => { let timeArr = boolCollection.value[key].map(item => new Date(item.localDateTime).getTime() ); timeList = timeList.length > timeArr.length ? timeList : timeArr; series.push({ name: findPropertyName(key), yAxisIndex: yAxis.length, type: "line", showSymbol: false, step: "middle", data: boolCollection.value[key].map(item => [ item.localDateTime, item.value ]) }); }); yAxis.push({ name: transformI18n($t("IOT.pureBoolean")), type: "value", min: 1, max: 0, interval: 1, // 强制间隔为1 axisLine: { show: true, symbol: ["none", "arrow"], // 显示线的起始箭头,不显示结束箭头 lineStyle: { color: "rgba(117,127,158,0.8)" } }, axisTick: { show: false }, axisLabel: { formatter: val => (val ? "true" : "false") } }); } const minTime = Math.min(...timeList); const maxTime = Math.max(...timeList); const timeRange = maxTime - minTime; // 设置固定刻度数 const interval = timeRange / 5; // 6 个点需要 5 个间隔 setOptions({ title: { text: props.displayName }, tooltip: { trigger: "axis", formatter: function (params) { let result = params[0].axisValueLabel + "<br/>"; params.forEach(function (item) { result += ` <div style=" display:flex; align-items:center; margin:3px 0; "> <span style=" display:inline-block; width:10px; height:10px; border-radius:50%; background:${item.color || "#666"}; margin-right:8px; "></span> <span style="flex:1;">${item.seriesName}</span> <span style="font-weight:bold;margin-left:10px;">${item.value[1]}</span> </div>`; }); return result; } }, legend: { show: true }, dataZoom: [ { type: "slider", show: true, left: "10%", right: "10%", bottom: "2%", start: 0, end: 100, backgroundColor: "#fff", borderColor: "#D9E0EF", fillerColor: "rgba(158,183,244,0.4)", dataBackground: { areaStyle: { color: "#F6F8FC" } }, handleStyle: { color: "#fff", borderColor: "#D9E0EF", borderWidth: 1, borderCap: "butt" } // "linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%)", }, { type: "inside" } ], grid: { left: "3%", right: "4%", bottom: "15%", containLabel: true }, color: "#409eff", xAxis: { name: "时间", type: "time", boundaryGap: false, min: minTime, max: maxTime, interval: interval, axisLabel: { show: true, formatter: function (value, index) { let time = dayjs(value).format("YYYY-MM-DD HH:mm:ss"); return time; } }, axisLine: { lineStyle: { color: "rgba(117,127,158,0.8)" }, symbol: ["none", "arrow"] }, nameGap: yAxis.length > 1 ? 40 : 15 } as any, yAxis: yAxis.length > 1 ? yAxis : yAxis[0], series: series }); window.addEventListener("resize", () => { resize(); }); }x轴设置固定刻度数无效
08-16
// 初始化ECharts实例 const chartDom = document.getElementById('radar-chart'); const myChart = echarts.init(chartDom); // 敌机数据配置 const enemyData = [ { name: 'T0021', points: generatePoints(10) // 生成10个航点 }, { name: 'T0025', points: generatePoints(10) }, { name: 'T0038', points: generatePoints(10) } ]; // 生成航点函数(极坐标:角度° + 半径km) function generatePoints(count) { const points = []; for (let i = 0; i < count; i++) { points.push([ Math.random() * 360, // 随机角度 [0, 360) 50 + Math.random() * 150 // 半径 [50, 200) ]); } return points; } // 配置项 const option = { polar: { radius: '70%' // 极坐标半径 }, angleAxis: { type: 'value', startAngle: 0, // 起始角度 clockwise: true, // 顺时针 axisLine: { show: true }, axisLabel: { show: true }, splitLine: { lineStyle: { color: 'rgba(0, 180, 0, 0.7)' } // 绿色扫描线 } }, radiusAxis: { min: 0, max: 200, axisLabel: { formatter: '{value} km' }, splitLine: { show: true } }, series: enemyData.map((enemy, idx) => ({ name: enemy.name, type: 'scatter', coordinateSystem: 'polar', symbolSize: 8, data: enemy.points, itemStyle: { color: ['#5470c6', '#91cc75', '#fac858'][idx] // 不同敌机颜色 }, // 标记最后一个航点 markPoint: { symbol: 'rect', symbolSize: [40, 25], // 红框尺寸 data: [{ coord: enemy.points[enemy.points.length - 1], // 最后一个点 name: enemy.name, itemStyle: { color: 'transparent', borderColor: '#ff0000', borderWidth: 2 }, // 引导线配置 label: { show: true, position: 'outside', formatter: enemy.name, fontSize: 14, fontWeight: 'bold', backgroundColor: 'rgba(0,0,0,0.7)', padding: [4, 8], color: '#fff', // 引导线样式 lineHeight: 20, rich: { line: { height: 40, lineStyle: { width: 1, color: '#ddd' } } } } }] } })) }; // 启用配置 myChart.setOption(option); // 窗口大小自适应 window.addEventListener('resize', () => myChart.resize()); 这个里面的rich.line 没有显示
09-18
import dash from dash import dcc, html from dash.dependencies import Input, Output, State import plotly.graph_objects as go import json import pandas as pd import os import numpy as np import math from shapely.geometry import Point, Polygon from shapely.ops import unary_union # ---------------------------------------------------------------------- # 1. 配置参数读取 # ---------------------------------------------------------------------- CONFIG_FILE = "simulation_parameter_case.json" try: with open(CONFIG_FILE, 'r') as f: config = json.load(f) isd = config["channel_parameters"]["isd"] display_cell_ids = [bs["cell_id"] for bs in config["bs_parameters"]] except Exception as e: print(f"Error: {e} - Using default values") isd = 200 display_cell_ids = [] # ---------------------------------------------------------------------- # 2. 7c3s小区拓扑类定义(添加精确四边形扇区) # ---------------------------------------------------------------------- class CellTopology: def __init__(self, isd=200): self.isd = isd self.radius = (self.isd / 2.0) / (np.cos(np.deg2rad(30))) # 基站位置 self.bs_loc_set = np.array([ [0.0, 0.0], # 中心基站 [-math.sqrt(3) * self.isd / 2.0, self.isd / 2.0], # 左上 [0.0, self.isd], # 上 [math.sqrt(3) * self.isd / 2.0, self.isd / 2.0], # 右上 [math.sqrt(3) * self.isd / 2.0, -self.isd / 2.0], # 右下 [0.0, -self.isd], # 下 [-math.sqrt(3) * self.isd / 2.0, -self.isd / 2.0] # 左下 ]) # 六边形顶点 self.center_bs_hexgon_vertex = np.array([ [self.radius, 0.0], [self.radius / 2.0, self.isd / 2.0], [-self.radius / 2.0, self.isd / 2.0], [-self.radius, 0.0], [-self.radius / 2.0, -self.isd / 2.0], [self.radius / 2.0, -self.isd / 2.0], [self.radius, 0.0] ]) # 扇区分割线 self.center_sector_split_line = np.array([ [0.0, 0.0], [self.radius, 0.0], # 0° [0.0, 0.0], [-self.radius / 2.0, self.isd / 2.0], # 120° [0.0, 0.0], [-self.radius / 2.0, -self.isd / 2.0] # 240° ]) # 存储每个扇区的四边形多边形 self.sector_polygons = {} current_cell_id = 0 # 扇区角度定义 sector_angles = [0, 120, 240] for bs_id in range(7): bs_loc = self.bs_loc_set[bs_id] hex_vertices = self.center_bs_hexgon_vertex + bs_loc # 为每个扇区创建四边形多边形 for i, angle in enumerate(sector_angles): # 扇区起始点和结束点 start_idx = i end_idx = (i + 1) % 3 # 扇区四边形顶点:基站中心 + 三个六边形顶点 quad_points = np.array([ bs_loc, # 基站中心 hex_vertices[start_idx * 2], hex_vertices[start_idx * 2 + 1], hex_vertices[end_idx * 2] ]) self.sector_polygons[current_cell_id] = quad_points current_cell_id += 1 # 使用配置文件中的ISD值初始化拓扑 topology = CellTopology(isd=isd) # ---------------------------------------------------------------------- # 3. UE轨迹工具初始设置 # ---------------------------------------------------------------------- JSON_FILE = "user_specific_parameter.json" # 初始化JSON文件 def initialize_json_file(): if not os.path.exists(JSON_FILE) or os.stat(JSON_FILE).st_size == 0: default_data = { "ue_trajectories": { "trajectory_interval_second": 1, "interpolation_interval_second": 0.01, "trajectory_list": [], "isd": isd, # 添加ISD信息 "display_cell_ids": display_cell_ids # 添加显示的cell_id信息 } } with open(JSON_FILE, 'w') as f: json.dump(default_data, f, indent=4) else: try: with open(JSON_FILE, 'r') as f: data = json.load(f) if "ue_trajectories" in data: # 更新当前ISD和显示的cell_id data["ue_trajectories"]["isd"] = isd data["ue_trajectories"]["display_cell_ids"] = display_cell_ids data["ue_trajectories"]["trajectory_list"] = [] else: data["ue_trajectories"] = { "trajectory_interval_second": 1, "interpolation_interval_second": 0.01, "trajectory_list": [], "isd": isd, "display_cell_ids": display_cell_ids } with open(JSON_FILE, 'w') as f: json.dump(data, f, indent=4) except Exception as e: print(f"初始化JSON文件错误: {e}") default_data = { "ue_trajectories": { "trajectory_interval_second": 1, "interpolation_interval_second": 0.01, "trajectory_list": [], "isd": isd, "display_cell_ids": display_cell_ids } } with open(JSON_FILE, 'w') as f: json.dump(default_data, f, indent=4) # 初始化JSON文件 initialize_json_file() # ---------------------------------------------------------------------- # 4. 背景点生成函数(只生成指定扇区内的点) # ---------------------------------------------------------------------- def generate_background_points(display_cell_ids, density=5): """只为显示的扇区生成背景点""" display_polygons = [] # 获取所有要显示扇区的多边形 for cell_id in display_cell_ids: if cell_id in topology.sector_polygons: quad_points = topology.sector_polygons[cell_id] display_polygons.append(Polygon(quad_points)) if not display_polygons: return [], [] # 合并所有多边形 combined_poly = unary_union(display_polygons) # 计算边界 min_x, min_y, max_x, max_y = combined_poly.bounds min_x -= 10 max_x += 10 min_y -= 10 max_y += 10 # 生成网格点 x_coords = np.linspace(min_x, max_x, int((max_x - min_x) * density)) y_coords = np.linspace(min_y, max_y, int((max_y - min_y) * density)) points_x = [] points_y = [] # 筛选在合并多边形内的点 for x in x_coords: for y in y_coords: p = Point(x, y) if combined_poly.contains(p): points_x.append(x) points_y.append(y) return points_x, points_y # 生成指定扇区的背景点 BG_POINTS_X, BG_POINTS_Y = generate_background_points(display_cell_ids, density=2) # ---------------------------------------------------------------------- # 5. 小区拓扑绘图函数(只绘制指定四边形扇区) # ---------------------------------------------------------------------- def create_initial_figure(all_trajectories_data=[]): fig = go.Figure() # 收集所有要显示的扇区顶点坐标 all_x = [] all_y = [] # 1. 只绘制指定的四边形扇区 for cell_id in display_cell_ids: if cell_id in topology.sector_polygons: quad_points = topology.sector_polygons[cell_id] all_x.extend(quad_points[:, 0]) all_y.extend(quad_points[:, 1]) # 闭合多边形(第一个点添加到末尾) x_quad = list(quad_points[:, 0]) + [quad_points[0, 0]] y_quad = list(quad_points[:, 1]) + [quad_points[0, 1]] # 添加四边形扇区边界 fig.add_trace(go.Scatter( x=x_quad, y=y_quad, mode='lines', line=dict(color='green', width=2), name=f'Cell {cell_id}', hoverinfo='skip', showlegend=False )) # 2. 添加扇区ID标签 labels_x = [] labels_y = [] labels_text = [] for cell_id in display_cell_ids: if cell_id in topology.sector_polygons: quad_points = topology.sector_polygons[cell_id] # 标签位置:四边形中心点 center_x = np.mean(quad_points[:, 0]) center_y = np.mean(quad_points[:, 1]) labels_x.append(center_x) labels_y.append(center_y) labels_text.append(f"Cell {cell_id}") # 添加中心点到坐标集合 all_x.append(center_x) all_y.append(center_y) fig.add_trace(go.Scatter( x=labels_x, y=labels_y, mode='text', text=labels_text, textfont=dict(size=12, color='black'), textposition='middle center', hoverinfo='skip', showlegend=False )) # 3. 添加透明背景点 fig.add_trace(go.Scatter( x=BG_POINTS_X, y=BG_POINTS_Y, mode='markers', marker=dict(size=5, color='rgba(0,0,0,0)', opacity=0), name='Click Area', hoverinfo='none', customdata=[[x, y] for x, y in zip(BG_POINTS_X, BG_POINTS_Y)], unselected=dict(marker={'opacity': 0}), selected=dict(marker={'color': 'rgba(255, 0, 0, 0.5)', 'opacity': 0.5, 'size': 8}), showlegend=False )) # 添加背景点到坐标集合 all_x.extend(BG_POINTS_X) all_y.extend(BG_POINTS_Y) # 4. 改进的自适应范围计算(解决左右太窄问题) if all_x and all_y: # 确保有数据点 min_x = min(all_x) max_x = max(all_x) min_y = min(all_y) max_y = max(all_y) # 计算实际范围 x_range = max_x - min_x y_range = max_y - min_y # 计算屏幕宽高比(假设16:9) screen_ratio = 16 / 9 # 计算数据宽高比 data_ratio = x_range / y_range if y_range != 0 else 1 # 调整范围以适应屏幕比例 if data_ratio > screen_ratio: # 数据比屏幕宽,需要增加高度 required_height = x_range / screen_ratio height_difference = max(0, (required_height - y_range) / 2) min_y -= height_difference max_y += height_difference else: # 数据比屏幕窄,需要增加宽度 required_width = y_range * screen_ratio width_difference = max(0, (required_width - x_range) / 2) min_x -= width_difference max_x += width_difference # 添加最小边距 margin = min(x_range, y_range) * 0.05 x_range = [min_x - margin, max_x + margin] y_range = [min_y - margin, max_y + margin] else: # 默认范围(宽屏比例) x_range = [-200, 200] y_range = [-112.5, 112.5] # 5. 最大化显示布局设置 fig.update_layout( title_text=f'Selected Cells: {display_cell_ids} - UE #{len(all_trajectories_data) + 1}', xaxis_title='X Coordinate (m)', yaxis_title='Y Coordinate (m)', xaxis=dict( range=x_range, scaleanchor='y', scaleratio=1, constrain='domain', automargin=True # 自动调整边距 ), yaxis=dict( range=y_range, constrain='domain', automargin=True # 自动调整边距 ), dragmode='select', template='plotly_white', clickmode='event+select', autosize=True, height=None, # 最小化边距,特别是左右边距 margin=dict(l=0, r=0, t=40, b=0), # 布局优化参数 paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', separators='.,' ) return fig # ---------------------------------------------------------------------- # 6. Dash应用布局 # ---------------------------------------------------------------------- app = dash.Dash(__name__) app.layout = html.Div([ html.Div([ html.H1(f"Cell Visualization Tool (ISD={isd}m)", style={'margin': '5px 0', 'padding': '0'}), html.P(f"Displayed Cells: {display_cell_ids}", style={'font-weight': 'bold', 'margin': '0', 'padding': '0 0 5px 0'}) ], style={'flex-shrink': 0}), # 防止标题区域压缩 # 图形容器 - 占据所有剩余空间 html.Div([ dcc.Graph( id='topology-graph', figure=create_initial_figure(), config={'displayModeBar': True, 'displaylogo': False}, style={ 'height': '100%', 'width': '100%', 'min-height': '400px' # 确保最小高度 } ) ], style={ 'flex-grow': 1, # 关键:占据所有可用空间 'position': 'relative', 'overflow': 'hidden' }), html.Div(id='selected-data-output', style={'margin': '5px 0', 'flex-shrink': 0}), html.Div([ html.Button('Save Current UE Trajectory', id='save-clear-button', n_clicks=0, style={'margin-right': '10px', 'padding': '8px', 'background-color': '#4CAF50', 'color': 'white'}), html.Button('Export to JSON', id='export-json-button', n_clicks=0, style={'padding': '8px', 'background-color': '#008CBA', 'color': 'white'}), ], style={'margin': '10px 0', 'flex-shrink': 0}), dcc.Store(id='current-ue-store', data=[]), dcc.Store(id='all-trajectories-store', data=[]), dcc.Store(id='last-point-coords', data={'x': None, 'y': None}) ], style={ 'height': '100vh', 'display': 'flex', 'flex-direction': 'column', 'padding': '10px', 'box-sizing': 'border-box', # 确保内边距不影响整体尺寸 'overflow': 'hidden' }) # ---------------------------------------------------------------------- # 7. Dash回调函数 # ---------------------------------------------------------------------- @app.callback( [Output('current-ue-store', 'data', allow_duplicate=True), Output('last-point-coords', 'data', allow_duplicate=True)], [Input('topology-graph', 'selectedData')], [State('current-ue-store', 'data'), State('topology-graph', 'figure')], # 添加对图形状态的依赖 prevent_initial_call=True ) def handle_graph_select(selectedData, current_ue_data, figure): if selectedData is None or not selectedData['points']: return dash.no_update, dash.no_update last_point = selectedData['points'][-1] # 动态计算背景点轨迹的曲线编号 background_curve_number = len(display_cell_ids) + 1 # 四边形扇区 + 标签轨迹 # 确认选择来自"点击区域"轨迹 if last_point.get('curveNumber') != background_curve_number: return dash.no_update, dash.no_update x = last_point['x'] y = last_point['y'] z = 1.5 new_point = [round(x, 4), round(y, 4), z] if current_ue_data and new_point == current_ue_data[-1]: return dash.no_update, dash.no_update updated_trajectory = current_ue_data + [new_point] return updated_trajectory, {'x': round(x, 4), 'y': round(y, 4)} @app.callback( [Output('topology-graph', 'figure', allow_duplicate=True), Output('selected-data-output', 'children')], [Input('current-ue-store', 'data'), Input('last-point-coords', 'data')], [State('all-trajectories-store', 'data')], prevent_initial_call=True ) def update_graph_and_display(current_ue_data, last_point_coords, all_trajectories_data): fig = create_initial_figure(all_trajectories_data) if current_ue_data: df = pd.DataFrame(current_ue_data, columns=['x', 'y', 'z']) fig.add_trace(go.Scatter( x=df['x'], y=df['y'], mode='lines+markers', marker=dict(size=8, color='red', symbol='circle'), line=dict(color='red', width=2), name='UE Trajectory', hoverinfo='text', text=[f'({x}, {y})' for x, y in zip(df['x'], df['y'])], showlegend=True )) if last_point_coords['x'] is not None: display_text = f"Last Point: X={last_point_coords['x']}, Y={last_point_coords['y']}, Height=1.5m" else: display_text = "Drag/click on the chart area to draw UE trajectory..." return fig, display_text @app.callback( [Output('current-ue-store', 'data', allow_duplicate=True), Output('all-trajectories-store', 'data', allow_duplicate=True)], [Input('save-clear-button', 'n_clicks')], [State('current-ue-store', 'data'), State('all-trajectories-store', 'data')], prevent_initial_call=True ) def save_and_clear_trajectory(n_clicks, current_ue_data, all_trajectories_data): if n_clicks > 0: if not current_ue_data: return [], all_trajectories_data updated_all_trajectories = all_trajectories_data + [current_ue_data] new_current_ue_data = [] return new_current_ue_data, updated_all_trajectories return dash.no_update, dash.no_update @app.callback( Output('export-json-button', 'children'), [Input('export-json-button', 'n_clicks')], [State('all-trajectories-store', 'data')], prevent_initial_call=True ) def export_to_json(n_clicks, all_trajectories_data): if n_clicks > 0: try: with open(JSON_FILE, 'r') as f: data = json.load(f) data["ue_trajectories"]["trajectory_list"] = all_trajectories_data with open(JSON_FILE, 'w') as f: json.dump(data, f, indent=4) num_trajectories = len(all_trajectories_data) return f"Exported {num_trajectories} trajectories to {JSON_FILE}" except Exception: return "Export failed!" return 'Export All Trajectories to JSON File' # ---------------------------------------------------------------------- # 7. 运行应用 # ---------------------------------------------------------------------- if __name__ == '__main__': print(f"Please visit http://127.0.0.1:8050/") app.run(debug=True) 在上面代码中,在生成的页面里最大化绘制的小区拓扑图,最好让它占满整个html页面
最新发布
11-01
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值