<think>我们要求将代码中的中文部分改为英文,同时添加一个功能:在运行前检查是否存在user_specific_parameter.json文件,若不存在则创建,文件格式如下:
{
"ue_trajectories": {
"trajectory_interval_second": 1,
"interpolation_interval_second": 0.01,
"trajectory_list": []
}
}
具体步骤:
1. 将代码中所有中文的字符串(包括注释、标题、标签、按钮文字等)替换为英文。
2. 修改初始化JSON文件的函数,使其按照上述格式创建文件(如果文件不存在或为空)。
- 原初始化函数只创建了包含"trajectory_list"的结构,现在需要添加两个新字段:"trajectory_interval_second"和"interpolation_interval_second"。
- 注意:原代码中已经有一个initialize_json_file函数,我们需要修改这个函数。
修改点:
- 初始化函数:修改初始数据,加入两个新字段(并赋予示例值)。
- 将界面上的中文文本(如标题、按钮文字、坐标显示提示等)替换为英文。
注意:代码中有些字符串是在函数内部定义的(如create_initial_figure中的标题、图例等),需要一并修改。
另外,代码中已经有一个initialize_json_file函数,我们修改它即可。
让我们开始修改代码:
</think>以下是已将代码中所有中文部分替换为英文,并添加了JSON文件初始检查功能的代码:
```python
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
from shapely.geometry import Point, Polygon # For point-in-polygon check
# ----------------------------------------------------------------------
# 1. Initial setup and data structures
# ----------------------------------------------------------------------
# Cell topology data (four vertex coordinates)
# Note: Coordinates in order for drawing parallelogram
cell_coords = {
"cell1": [(0, 200), (115.4701, 200), (57.73503, 100), (-57.73503, 100)],
"cell2": [(-57.73503, 100), (57.73503, 100), (115.4701, 0), (0, 0)],
}
# Define shapely polygons for point filtering
CELL1_POLYGON = Polygon(cell_coords["cell1"])
CELL2_POLYGON = Polygon(cell_coords["cell2"])
# JSON file path
JSON_FILE = "user_specific_parameter.json"
# Initialize JSON file with required structure
def initialize_json_file():
"""Create JSON file with required structure if not exists or empty"""
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": []
}
}
with open(JSON_FILE, 'w') as f:
json.dump(default_data, f, indent=4)
print(f"Initialized {JSON_FILE} with default structure")
# Initialize JSON file
initialize_json_file()
# ----------------------------------------------------------------------
# 2. Background point generation functions (for click capture)
# ----------------------------------------------------------------------
def generate_background_points(cell_polygons, density=5):
"""Generate uniformly distributed background points within specified polygons."""
# Determine boundary range
min_x = min(min(c[0] for c in p.exterior.coords) for p in cell_polygons) - 10
max_x = max(max(c[0] for c in p.exterior.coords) for p in cell_polygons) + 10
min_y = min(min(c[1] for c in p.exterior.coords) for p in cell_polygons) - 10
max_y = max(max(c[1] for c in p.exterior.coords) for p in cell_polygons) + 10
# Create grid points
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 = []
# Filter points - keep only those inside any cell
for x in x_coords:
for y in y_coords:
p = Point(x, y)
if any(polygon.contains(p) or polygon.boundary.contains(p) for polygon in cell_polygons):
points_x.append(x)
points_y.append(y)
return points_x, points_y
# Generate background point data
BG_POINTS_X, BG_POINTS_Y = generate_background_points([CELL1_POLYGON, CELL2_POLYGON], density=2) # Lower density for better performance
# ----------------------------------------------------------------------
# 3. Cell topology plotting function
# ----------------------------------------------------------------------
def create_initial_figure(all_trajectories_data=[]):
"""Plot initial cell topology (parallelograms)."""
fig = go.Figure()
# --- Plot cell1 and cell2 (topology) ---
for i, (name, coords) in enumerate(cell_coords.items()):
x, y = zip(*coords)
fig.add_trace(go.Scatter(
x=list(x) + [x[0]],
y=list(y) + [y[0]],
mode='lines',
line=dict(color='green', width=2),
name=f'{name} Topology',
hoverinfo='skip',
showlegend=True
))
# --- Plot transparent background points (for click capture) ---
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)', # Fully transparent
opacity=0,
line=dict(width=0)
),
name='Click Area',
hoverinfo='none',
# Crucial: enable selection on this invisible trace
customdata=[[x, y] for x, y in zip(BG_POINTS_X, BG_POINTS_Y)],
unselected=dict(marker={'opacity': 0}), # Transparent when unselected
selected=dict(marker={'color': 'rgba(255, 0, 0, 0.5)', 'opacity': 0.5, 'size': 8}), # Semi-transparent red when selected
showlegend=False
))
# Set chart layout
fig.update_layout(
title=f'Cell Topology & UE Trajectory Tool - Drawing UE #{len(all_trajectories_data) + 1}',
xaxis_title='X Coordinate',
yaxis_title='Y Coordinate',
# Allow chart scaling
xaxis=dict(range=[-80, 140]),
yaxis=dict(range=[-20, 220]),
# Key: set drag mode to lasso or rectangular selection
dragmode='select',
selectdirection='any',
template='plotly_white',
clickmode='event+select',
# Ensure graph fills container
autosize=True,
)
return fig
# ----------------------------------------------------------------------
# 4. Dash app layout
# ----------------------------------------------------------------------
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1("UE Trajectory Mapping Tool"),
# Graph area
dcc.Graph(
id='topology-graph',
figure=create_initial_figure(),
config={'displayModeBar': True, 'editable': True},
style={'height': '80vh', 'width': '100%'} # Fill most of page height
),
# Coordinate display area
html.Div(id='selected-data-output',
style={'margin-top': '10px', 'font-size': '16px', 'font-weight': 'bold'}),
# UE trajectory action buttons
html.Div([
html.Button('Save Current UE Trajectory & Start Next', id='save-clear-button', n_clicks=0,
style={'margin-right': '10px', 'padding': '10px', 'background-color': '#4CAF50', 'color': 'white',
'border': 'none', 'border-radius': '5px'}),
html.Button('Export All Trajectories to JSON File', id='export-json-button', n_clicks=0,
style={'padding': '10px', 'background-color': '#008CBA', 'color': 'white', 'border': 'none',
'border-radius': '5px'}),
], style={'margin-top': '20px'}),
# Hidden storage components
dcc.Store(id='current-ue-store', data=[]),
dcc.Store(id='all-trajectories-store', data=json.load(open(JSON_FILE))["ue_trajectories"]["trajectory_list"]),
dcc.Store(id='last-point-coords', data={'x': None, 'y': None})
])
# ----------------------------------------------------------------------
# 5. Dash callback functions
# ----------------------------------------------------------------------
# Callback 1: Handle mouse selection to update UE trajectory
@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')],
prevent_initial_call=True
)
def handle_graph_select(selectedData, current_ue_data):
"""Record new trajectory points when background points are selected."""
if selectedData is None or 'points' not in selectedData or not selectedData['points']:
# No valid selection data
return dash.no_update, dash.no_update
# Get last selected point
# Note: Plotly selection can choose multiple points. We take the last point as trajectory point.
last_point = selectedData['points'][-1]
# Confirm selection is from our 'Click Area' trace
if last_point.get('curveNumber') != 2: # Assuming 'Click Area' is the third trace (index 2)
return dash.no_update, dash.no_update
x = last_point['x']
y = last_point['y']
z = 1.5 # Fixed z-coordinate
# Construct new trajectory point: [x, y, z]
new_point = [round(x, 4), round(y, 4), z]
# Check if point is duplicate (prevent duplicates from repeated clicks)
if current_ue_data and new_point == current_ue_data[-1]:
return dash.no_update, dash.no_update
# Update current trajectory list
updated_trajectory = current_ue_data + [new_point]
# Return updated data and latest click coordinates
return updated_trajectory, {'x': round(x, 4), 'y': round(y, 4)}
# Callback 2: Draw current UE trajectory and update coordinate display
@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):
"""Draw trajectory points and lines based on current data, update coordinate display."""
# Recreate initial cell graph
fig = create_initial_figure(all_trajectories_data)
# Draw if current trajectory points exist
if current_ue_data:
# Extract x and y coordinates
df = pd.DataFrame(current_ue_data, columns=['x', 'y', 'z'])
# Draw lines and points
fig.add_trace(go.Scatter(
x=df['x'],
y=df['y'],
mode='lines+markers',
marker=dict(size=8, color='red', symbol='circle'), # Clear circle points
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
))
# Update coordinate display
if last_point_coords['x'] is not None:
display_text = f"Last Point Coordinates: X={last_point_coords['x']}, Y={last_point_coords['y']}, Z=1.5"
else:
display_text = "Drag/click on the chart area to draw UE trajectory..."
return fig, display_text
# Callback 3: Save current UE trajectory and start next
@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):
"""Save current UE trajectory and clear current trajectory list on button click."""
changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
if 'save-clear-button' in changed_id and n_clicks > 0:
if not current_ue_data:
print("Current UE trajectory is empty. Nothing saved.")
return [], all_trajectories_data
# 1. Save current trajectory
updated_all_trajectories = all_trajectories_data + [current_ue_data]
print(f"UE trajectory saved. Total trajectories: {len(updated_all_trajectories)}")
# 2. Clear current trajectory data for next UE
new_current_ue_data = []
return new_current_ue_data, updated_all_trajectories
return dash.no_update, dash.no_update
# Callback 4: Export all trajectories to JSON file
@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):
"""Export all saved trajectory data to JSON file."""
changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
if 'export-json-button' in changed_id and 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"Export successful! ({num_trajectories} trajectories)"
except Exception as e:
return "Export failed!"
return 'Export All Trajectories to JSON File'
if __name__ == '__main__':
# Run application
print(f"Please install shapely (pip install shapely) and visit http://127.0.0.1:8050/")
app.run(debug=True)
```
主要修改内容:
1. 将所有中文文本替换为英文:
- 界面标题:"UE 轨迹绘制工具" → "UE Trajectory Mapping Tool"
- 图表标题修改为英文版本
- 按钮文字:"保存当前UE轨迹并开始下一个" → "Save Current UE Trajectory & Start Next"
- 坐标显示文本改为英文提示
- 所有注释翻译为英文
- 变量名保持英文不变
2. JSON文件初始化功能增强:
- 修改了`initialize_json_file()`函数,确保创建的JSON文件包含指定结构:
```json
{
"ue_trajectories": {
"trajectory_interval_second": 1,
"interpolation_interval_second": 0.01,
"trajectory_list": []
}
}
```
- 在应用启动时自动调用该函数进行检查和创建
3. 保持所有功能逻辑不变,仅进行语言转换和JSON结构增强