Хочу создать приложение интерактивной карты для игры War Thunder. Данные для карты получаю с локалхоста двумя json файлами: 1) координаты объектов (x и y), 2) Свойства карты: параметры карты (grid_steps, grid_zero, map_max, map_min.) и изображение карты в формате img. Ранее удалось реализовать отображение карты через matplotlib, однако из-за необходимости обновлять карту минимум раз в 5 секунд (объекты движутся) пришлось перейти на pygame (изображение получалось рваным, фризы каждые 5 секунд). Получилось импортировать и открывать карту, но вот с объектами и сеткой квадратов какая-то беда. Квадраты слишком больших размеров и выходят за пределы изображения карты, а все объекты почему-то скапливаются и отображаются в левом верхнем углу, хотя они явно должны быть где-то ниже и не налеплены один на другого. Мои попытки "исправить положение" привели меня к тому что я удалил сетку квадратов и попробовал сделать так чтобы координаты объектов считались от grid_zero. Но тогда объекты и вовсе пропали из поля зрения. Я попробовал разобраться почему так и прикрутил matplotlib для того чтобы посмотреть куда смещаются объекты, но оказалось что они и так уже находятся на карте, но я их при этом не вижу. Собственно я немного запутался и не понимаю как грамотно нужно реализовать работу с получаемыми данными. Внятного описания того что за что отвечает у меня нет, но приблизительно я понял так, что grid_steps - это масштаб одного квадрата координатной сетки, grid_zero - начало отсчёта, map_max и map_min - оси по Х и Y. Я подозреваю что то-ли у меня изображение карты перекрывает объекты, то ли я неправильно задал параметры для карты. Ниже привожу код на pygame:
map_info = MapInformation()
map_info.grid_steps = list(map(float, map_info_data.get('grid_steps', [])))
map_info.grid_zero = list(map(float, map_info_data.get('grid_zero', [])))
map_info.map_max = list(map(float, map_info_data.get('map_max', [])))
map_info.map_min = list(map(float, map_info_data.get('map_min', [])))
map_info.valid = map_info_data.get('valid', False)
# Create MapObjects and store their positions
map_objects = []
for obj_data in map_obj_data:
map_object = MapObject(
type=obj_data.get('type', ''),
color=obj_data.get('color', ''),
colors=obj_data.get('color[]', []),
blink=obj_data.get('blink', 0),
icon=obj_data.get('icon', ''),
icon_bg=obj_data.get('icon_bg', ''),
x=float(obj_data.get('x', 0.0)),
y=float(obj_data.get('y', 0.0)),
dx=float(obj_data.get('dx', 0.0)),
dy=float(obj_data.get('dy', 0.0)),
sx=float(obj_data.get('sx', 0.0)),
sy=float(obj_data.get('sy', 0.0)),
ex=float(obj_data.get('ex', 0.0)),
ey=float(obj_data.get('ey', 0.0))
)
map_objects.append(map_object)
return map_info, map_img, map_objects
except requests.RequestException as e:
print(f"Error loading data: {e}")
return None, None, None
def to_absolute(x, y, map_info):
map_min_x = map_info.map_min[0]
map_min_y = map_info.map_min[1]
map_max_x = map_info.map_max[0]
map_max_y = map_info.map_max[1]
grid_zero_x = map_info.grid_zero[0]
grid_zero_y = map_info.grid_zero[1]
abs_x = grid_zero_x + (map_min_x + x * (map_max_x - map_min_x) - map_min_x)
abs_y = grid_zero_y + (map_min_y + (1 - y) * (map_max_y - map_min_y) - map_min_y)
return abs_x, abs_y
def save_positions(map_objects):
# Save positions to a file or data structure for later plotting
positions = {
'x': [],
'y': [],
'sx': [],
'sy': [],
'ex': [],
'ey': []
}
for obj in map_objects:
positions['x'].append(obj.x)
positions['y'].append(obj.y)
positions['sx'].append(obj.sx)
positions['sy'].append(obj.sy)
positions['ex'].append(obj.ex)
positions['ey'].append(obj.ey)
return positions
def draw_objects(screen, map_info, map_objects, offset, zoom):
for obj in map_objects:
obj_type = obj.type
color = tuple(obj.colors) if obj.colors else tuple(obj.color)
if obj_type == "airfield":
sx, sy = to_absolute(obj.sx, obj.sy, map_info)
ex, ey = to_absolute(obj.ex, obj.ey, map_info)
sx = (sx - map_info.map_min[0]) * zoom + offset[0]
sy = (sy - map_info.map_min[1]) * zoom + offset[1]
ex = (ex - map_info.map_min[0]) * zoom + offset[0]
ey = (ey - map_info.map_min[1]) * zoom + offset[1]
pygame.draw.rect(screen, color, pygame.Rect(sx, sy, ex - sx, ey - sy))
elif obj_type == "aircraft":
x, y = to_absolute(obj.x, obj.y, map_info)
x = (x - map_info.map_min[0]) * zoom + offset[0]
y = (y - map_info.map_min[1]) * zoom + offset[1]
dx = obj.dx
dy = obj.dy
if obj.icon == "Player":
draw_aircraft(screen, x, y, dx, dy, (0, 255, 0), zoom)
else:
draw_aircraft(screen, x, y, dx, dy, color, zoom)
def draw_aircraft(screen, x, y, dx, dy, color, zoom):
size = 10 * zoom
points = [
(x + dx * size, y + dy * size),
(x - dy * size / 2, y + dx * size / 2),
(x + dy * size / 2, y - dx * size / 2),
]
pygame.draw.polygon(screen, color, points)
def plot_positions(map_info, positions, map_objects):
fig, ax = plt.subplots()
# Plot map area as green square
map_min_x, map_min_y = map_info.map_min
map_max_x, map_max_y = map_info.map_max
map_width = map_max_x - map_min_x
map_height = map_max_y - map_min_y
map_rect = plt.Rectangle((map_min_x, map_min_y), map_width, map_height, edgecolor='green', facecolor='none')
ax.add_patch(map_rect)
# Plot objects with labels
for obj in map_objects:
x_label = f"{obj.type} ({obj.icon})" if obj.icon else obj.type
if obj.type == "airfield":
sx, sy = obj.sx, obj.sy
ex, ey = obj.ex, obj.ey
ax.scatter([sx, ex], [sy, ey], color='red', label=x_label)
elif obj.type == "aircraft":
x, y = obj.x, obj.y
ax.scatter(x, y, color='red', label=x_label)
ax.set_xlim(map_min_x, map_max_x)
ax.set_ylim(map_min_y, map_max_y)
ax.set_aspect('equal', adjustable='box')
ax.set_xlabel('Map X Coordinate')
ax.set_ylabel('Map Y Coordinate')
ax.set_title('Object Positions on Map')
ax.legend()
plt.show()
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Map Viewer')
map_info, map_img, map_objects = load_data()
offset = [0, 0]
zoom = 1.0
zoom_step = 0.1
dragging = False
drag_start = (0, 0)
running = True
last_update = time.time()
def update_data():
global map_info, map_img, map_objects, last_update
while running:
if time.time() - last_update >= 5:
map_info, map_img, map_objects = load_data()
last_update = time.time()
update_thread = threading.Thread(target=update_data)
update_thread.start()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_PLUS or event.key == pygame.K_EQUALS:
zoom += zoom_step
elif event.key == pygame.K_MINUS:
zoom = max(0.1, zoom - zoom_step)
elif event.key == pygame.K_p and pygame.key.get_mods() & pygame.KMOD_CTRL:
# Save and plot positions on Ctrl + P
positions = save_positions(map_objects)
plot_positions(map_info, positions, map_objects)
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
dragging = True
drag_start = event.pos
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
dragging = False
elif event.type == pygame.MOUSEMOTION:
if dragging:
mouse_x, mouse_y = event.pos
offset[0] += mouse_x - drag_start[0]
offset[1] += mouse_y - drag_start[1]
drag_start = event.pos
if map_info and map_img and map_objects:
screen.fill((0, 0, 0))
screen.blit(pygame.transform.scale(map_img, (int(map_img.get_width() * zoom), int(map_img.get_height() * zoom))), offset)
draw_objects(screen, map_info, map_objects, offset, zoom)
pygame.display.flip()
else:
print("Waiting for data...")
pygame.quit()
running = False
update_thread.join()
Вот так выглядит вывод на matplotlib (pygame версия):
А вот как должен выглядеть (версия с matplotlib):